Skip to content

Components & applications

Components group provided API boundaries and declare which types, services, or components they use. A provide declaration says that an API element belongs to the component's provided boundary; it does not by itself assert that executable implementation code already exists. Applications compose components into a logical system boundary.

Complete example

cmn
package com.example.customer

import org.joinedworkz.facilities.common.base
import org.joinedworkz.facilities.common.base.api

platform Base

type Customer {
    id**: Id
}

service CustomerService {
    find(id*: Id): Customer
}

resource /customers as Customer[] by id {
    query()
    read()
}

component CustomerComponent uses CustomerService {
    provide /customers
}

component ReportingComponent uses CustomerComponent {
}

application CustomerApp {
    consists of {
        CustomerComponent
    }

    use {
        ReportingComponent
    }
}

CustomerComponent uses the service and provides the imported resource. What a cartridge generates from those declarations depends on the selected facility.

Component dependencies

Dependencies are written in the component header with uses; there is no depends keyword:

cmn
component CustomerComponent uses CustomerService, Customer {
}

A component can use types, services, and other components.

Component declaration structure

The component declaration can have a stereotype, label, header dependencies, and profile-defined properties:

text
[description] component [<stereotype>] Name ["Label"]
    [uses TypeOrServiceOrComponent, ...]
    [property ...] {
        ...
    }

Within the body, the grammar orders typed component properties, data, routes, fragment includes, operations, component members, and nested components. This order is not freely interchangeable. The specialized families and their facility-support boundaries are indexed in Specialized model elements.

Provided API boundaries

The public declaration for a resource is:

cmn
component CustomerComponent {
    provide /customers
}

provide /customers references a resource that is visible in the model. It assigns the resource to the component's provided boundary. Properties and an optional body can add metadata or behavioral descriptions for cartridges that support them. For example, Base can use a controller property as an OpenAPI tag, while SpringBoot can also use it when generating a controller.

Do not read provide as a language-level implementation statement. Whether controllers, handlers, diagrams, or other artifacts are generated is a facility-/cartridge-specific decision.

Component artifact namespaces

The public component metadata separates the target namespace from the CMN model namespace:

cmn
package com.example.inventory.composition

component InventoryBackend
    componentNamespace='com.example.inventory.runtime' {

    provide /inventory/items
        namespaceSuffix='items.v1'
        controller='ItemController'
}
  • package defines the technical CMN namespace used for model identity and imports.
  • componentNamespace defines a target namespace for artifacts derived from the component.
  • namespaceSuffix is a relative suffix for artifacts derived from this particular provide boundary.
  • controller is a logical, platform-neutral grouping name. A cartridge can use it for tags, diagrams, or a platform-native controller artifact.

These properties do not alter the HTTP resource path. Their concrete artifact mapping is part of the selected facility's contract.

On these scopes, the previous names remain compatibility aliases:

  • component basePackage is deprecated in favor of componentNamespace;
  • provide property subPackage is deprecated in favor of namespaceSuffix.

Do not use the deprecated names in new models.

Nested resource selection and the deepest boundary

A provide reference selects model elements along a resource tree. It is not a literal HTTP path expression. Identifiers declared with by affect the HTTP path when an item-relative resource or instance operation is reached, but they are not written into the provide selector:

cmn
type Item {
    itemId**: Id
}

type Inspection {
    inspectionId**: Id
}

resource /inventory {
    /items as Item[] by itemId {
        query()

        ./inspections as Inspection[] by inspectionId {
            query()
        }
    }
}

component InventoryBackend
    componentNamespace='com.example.inventory.runtime' {

    provide /inventory/items
        namespaceSuffix='items.v1'
        controller='ItemController'

    provide /inventory/items/inspections
        namespaceSuffix='inspections.v1'
        controller='InspectionController'
}

The second selector names the nested inspections resource even though its HTTP collection path includes the parent identifier: /inventory/items/{itemId}/inspections. Where provided boundaries overlap, the most specific, deepest matching boundary supplies the logical controller metadata for operations in that subtree. The parent boundary continues to cover the remainder of its tree.

All of these boundaries still belong to the same component. They therefore contribute to one component-scoped OpenAPI aggregate. A repeated final controller identity is not a declaration that separately provided boundaries should be merged into one generated controller.

Resources, services, and implement

Both resources and services can be assigned to a component:

cmn
package com.example.customer

import org.joinedworkz.facilities.common.base
import org.joinedworkz.facilities.common.base.api

platform Base

type Customer {
    id**: Id
}

service CustomerService {
    find(id*: Id): Customer
}

resource /customers as Customer[] by id {
    read()
}

component CustomerComponent {
    provide CustomerService
    provide /customers
}

The grammar also accepts implement in the same position. For a service, JoinedWorkz retains implement as an internal-feature marker in the canonical Core Model:

cmn
package com.example.internal

service InternalCustomerService {
}

component CustomerComponent {
    implement InternalCustomerService
}

For a resource path, the transformation does not retain an equivalent separate internal marker. Do not use implement /resource to infer a public versus internal resource contract. provide remains the documented form for the component API boundary.

Properties and command bodies can follow a provided feature. Their names and behavior are facility-defined; the core declaration alone does not promise an executable handler.

Base component-scoped OpenAPI

With the Base platform, every component with provided resources produces an additional component-scoped OpenAPI YAML and matching HTML viewer. The document aggregates all resource paths in that component's provide declarations, including resources imported from different CMN models.

The effective controller name of a provided boundary can contribute an OpenAPI operation tag. For overlapping parent and nested boundaries, the deepest matching boundary supplies that metadata for its subtree. Neither componentNamespace nor namespaceSuffix changes an OpenAPI path.

The same cartridge also creates a model-scoped document from the non-abstract resources declared directly in each CMN model. Applications do not create a third, application-wide aggregate.

Application composition

  • consists of { ... } lists components that belong to the application.
  • use { ... } or uses { ... } lists referenced components outside that composition.

The component lists are blocks; use CustomerComponent is not valid application syntax. Application declarations do not by themselves create a runnable application; that requires a facility and, where applicable, a project application shell.

An application can also have a description, stereotype, and profile-defined properties. Its three optional body sections are consists of, use/uses, and supports; they may appear in any order, but each at most once. Component and scenario references inside these blocks are separated by whitespace, not commas.

supports { ... } references scenarios. Scenario processing is a specialized facility capability rather than a universal application output contract; see Specialized model elements.

The component and application structure of the stable facility is shown in the buildable Spring Boot example.