Skip to content

Components and applications

This page explains how to model components and applications in the canonical model and how they relate to APIs and generation.

We focus on:

  • component definitions and provided endpoints
  • optional pseudo-code for documenting behaviour
  • applications as compositions of components
  • what generators typically do with this information

It builds on the concepts from:


1. Components

A component represents a deployable building block in your system. It groups API endpoints and (optionally) behaviour that belongs together.

Typical examples:

  • a backend service
  • a microservice or bounded context
  • a web API with a set of controllers

In the canonical model, a component:

  • provides resources/endpoints from your API model,
  • can be linked to implementation artefacts (e.g. Java controllers) via attributes like basePackage and controller,
  • can optionally contain pseudo-code to document the internal flow of a call for documentation and diagrams.

1.1 Basic component definition

A minimal component definition for the quickstart example looks like this:

text
package com.example.joindeworkz.quickstart.backend

import com.example.joindeworkz.quickstart

platform Base

component QuickstartBackend basePackage='com.example.joindeworkz.quickstart.webapp' {

    provide /hello subPackage='greeting.v1' controller="GreetingV1Controller" { }

}

Key points:

  • The package com.example.joindeworkz.quickstart.backend defines the namespace of the component.
  • The component is named QuickstartBackend.
  • The attribute basePackage indicates where the platform should place generated implementation artefacts (for example Java controllers) for this component.
  • The import com.example.joindeworkz.quickstart makes the API model (resource /hello) available so it can be provided by the component.
  • The platform Base selects the Base platform, which among other things includes the OpenAPI and diagram cartridges.

1.2 Provided endpoints

Inside a component you declare which API endpoints it provides. In the example:

text
provide /hello subPackage='greeting.v1' controller="GreetingV1Controller" { }

This means:

  • The component QuickstartBackend implements the resource /hello defined in the imported API model.
  • The implementation is grouped under the subPackage greeting.v1 below the component's basePackage (platforms use this to compute final package names).
  • The implementation class (e.g. a controller in a Spring Boot platform) is called GreetingV1Controller.

Different platforms may use this information in different ways. For example:

  • A Spring Boot platform could generate a GreetingV1Controller class in a package derived from basePackage and subPackage.
  • The OpenAPI cartridge of the Base platform can use the controller name as a tag in the generated OpenAPI documents.
  • Diagram cartridges can show which endpoints belong to which component.

2. Optional pseudo-code for behaviour

Components can optionally contain pseudo-code that documents the behaviour of individual endpoints. This pseudo-code is not executable business logic – it is used for diagrams and documentation.

Extending the previous example:

text
component QuickstartBackend basePackage='com.example.joindeworkz.quickstart.webapp' {

    provide /hello subPackage='greeting.v1' controller="GreetingV1Controller" {

        read {
            if 'is morning' {
                [[ return 'Good Morning' ]]
            } else 'is evening' {
                [[ return 'Good Evening' ]]
            }
        }
    }
}

Here:

  • The read block corresponds to the read endpoint on /hello.
  • Inside the block you describe the flow as pseudo-code:
    • conditions (if 'is morning', else 'is evening'),
    • actions ([[ return 'Good Morning' ]], [[ return 'Good Evening' ]]).

The diagram cartridge of the Base platform can turn this into a sequence-like diagram that visualises the flow of the endpoint. This is useful for:

  • documentation,
  • discussions with other developers or stakeholders,
  • understanding complex flows without reading implementation code.

Important:

  • Pseudo-code is optional. Components are still useful without it (for assigning endpoints to components and driving generation).
  • The exact pseudo-code syntax is intentionally lightweight and focused on readability. Details belong in the DSL reference and the documentation of the diagram cartridge.

3. Applications

An application is a top-level model element that represents a runnable system composed of components.

Applications are used to:

  • get an overview of which components belong to a specific app,
  • visualise dependencies between own and external components,
  • generate a combined OpenAPI document for all endpoints of the application,
  • generate an HTML page with an embedded OpenAPI viewer for that document.

3.1 Basic application definition

Continuing the quickstart example:

text
application QuickstartApp {

    consists of {

        QuickstartBackend
    }

    use {
       // referenced components
    }

}

Meaning:

  • The application is called QuickstartApp.
  • It consists of the component QuickstartBackend. These are the components that are part of the application itself.
  • The optional use section can list external components that this application depends on (for example shared or third-party components).

Platforms and cartridges can use this information to:

  • draw a component diagram showing the application and its components,
  • highlight dependencies between internal and external components,
  • determine which endpoints belong to a given application when generating aggregated artefacts.

3.2 Aggregated OpenAPI and viewer

Using the information from components and applications, the OpenAPI cartridge in the Base platform can:

  • generate an OpenAPI YAML that contains all endpoints of the application,
  • group operations under tags derived from controller names (such as GreetingV1Controller),
  • generate an HTML file that embeds an OpenAPI viewer for this document.

This gives you a convenient, application-centric view of your API:

  • one document per application,
  • grouped by controllers/tags,
  • directly derived from the canonical model.

4. How components and applications fit in

Putting it all together:

  • APIs (resource / service) define what is exposed.
  • Components define who provides which part of the API and how this maps to implementation artefacts (controllers, packages, etc.).
  • Optional pseudo-code inside components documents how calls are handled and enables generation of behavioural diagrams.
  • Applications assemble components into runnable systems and define the scope for application-wide artefacts such as combined OpenAPI documents and component diagrams.

Because components and applications live in the canonical model, you can:

  • reuse the same component/application structure across different platforms,
  • generate diagrams, OpenAPI and implementation artefacts from a single source of truth,
  • evolve your technical stack while keeping the logical architecture stable.

5. Next steps

From here you can:

  • read about Profiles & platforms (.profil) to see how platforms configure cartridges (including diagram and API generators) and interpret component/application models;
  • explore Modeling with JoinedWorkz Studio for details on how components and applications are visualised and edited in the Studio;
  • consult the CMN reference for the exact syntax of component and application definitions and pseudo-code blocks.