Skip to content

Services and operations

Services group callable business operations. They are distinct from resources and resource methods: an operation can declare a typed result, while a resource method can declare HTTP-oriented response codes and representations.

1. Complete syntax example

cmn
package com.example.customer

import org.joinedworkz.facilities.common.base

platform Base

type Customer {
    id**: Id
    name*: Name
}

type SearchCriteria {
    name: Name
}

abstract service BaseCustomerService with Customer {
    find(id*: Id): Customer
}

service CustomerService with Customer
    uses SearchCriteria
    extends BaseCustomerService {

    search(criteria*: SearchCriteria, limit: Integer): Customer[]
    findActive(id*: Id): Customer
        augments BaseCustomerService.find
        uses SearchCriteria
}

The snippet demonstrates every service-header relationship. Its generator meaning still depends on the selected platform.

2. Service declaration

text
[description] [abstract] service [<stereotypes>] Name
    [with MainType]
    [uses TypeOrServiceOrComponent, ...]
    [property ...]
    [extends BaseService] {
        operation ...
    }
  • with associates one complex type as the service's main type.
  • uses declares dependencies on types, services, or components.
  • extends references one base service.
  • Properties occur before extends in the concrete syntax.
  • Descriptions, stereotypes, and abstract use the common declaration notation.

with supplies the field scope for field-based parameters, and uses is represented as a dependency in the canonical Core Model. The grammar accepts abstract and extends, but JoinedWorkz does not transfer those two service attributes into the canonical Core Model. Do not rely on service abstraction or inheritance for generated output.

3. Operation declaration

An operation is written directly by name; there is no operation keyword:

text
[description] [abstract] [<stereotypes>] name(parameters)
    [: ResultType [cardinality]]
    [augments BaseOperation]
    [uses TypeOrServiceOrComponent, ...]
    [property ...]
    [statement ...]

Documentation immediately before the operation belongs to that operation. Use the platform-independent documentation syntax; the selected facility decides which summaries, descriptions and examples are emitted into target artifacts.

Examples:

cmn
package com.example.calculation

import org.joinedworkz.facilities.common.base

platform Base

type Invoice {
    id**: Id
    total*: Decimal
}

service InvoiceService with Invoice {
    find(id*: Id): Invoice
    search(maximum: Decimal): Invoice[]
    calculate(total*: Decimal): Decimal => total;;
}
  • id*: Id is a mandatory parameter.
  • maximum: Decimal has no explicit mandatory marker.
  • : Invoice is a single typed result.
  • : Invoice[] is a result with 0..* cardinality.
  • augments references another operation.
  • uses has the same dependency target kinds as service-level uses.
  • An operation statement starts with => and ends with ;;. CMN transfers its normalized body as opaque text; only a supporting cartridge can assign executable meaning to it.

4. Operation statements and augmentation

An operation can contain one or more statement bodies. A body without a name is written directly after the operation declaration:

cmn
package org.example.reporting

type Report {
    content: String
}

service ReportService {
    render(): Report =>
        OPAQUE REPORT BODY;;
}

A name before => identifies a statement part:

cmn
package org.example.reporting

type Report {
    content: String
}

service ReportService {
    renderOpenItems(): Report
        criteria => STATUS = OPEN;;
        ordering => CREATED_AT DESC;;
}

=> and ;; delimit the body. They are not part of the value transferred to the Core Model. CMN also removes structural indentation from multiline bodies. The remaining content is not parsed as another CMN expression language.

The augments relationship connects an operation to a base operation. This allows a facility to interpret the base statement and the named statement parts together:

cmn
package org.example.reporting

import org.joinedworkz.facilities.common.base

platform Base

type Report {
    content: String
}

service ReportService {
    reportTemplate(): Report =>
        OPAQUE REPORT BODY;;

    openItemsReport(): Report augments reportTemplate
        criteria => STATUS = OPEN;;
}

At the CMN and Core Model level this means only that:

  • reportTemplate owns one unnamed statement body;
  • openItemsReport references reportTemplate as its base operation; and
  • openItemsReport owns one named statement part, criteria.

augments does not define a universal text-merge algorithm and does not copy the base operation's parameters, result, properties, or stereotypes into the derived declaration. A facility that supports executable statements defines the statement language, composition rules, generated artifacts and runtime contract. For the stable SpringBoot platform, see Repository statements and composition.

Operation statement parts are distinct from component fragments included with ... Fragment. They use different grammar elements and have separate facility contracts.

5. Parameters

An operation parameter has one of two forms:

cmn
package com.example.parameters

import org.joinedworkz.facilities.common.base

platform Base

type Customer {
    customerId**: Id
}

service CustomerService with Customer {
    byExplicitType(id*: Id): Customer
    byField(customerId): Customer
    byFieldWithOverride(id ..customerId as String): Customer
    mergeLabels(labels: String[String]): String[String]
}
  • name*: Type declares a parameter and its type.
  • A field reference such as customerId reuses the field definition.
  • id ..customerId gives the reused field a parameter-local name.
  • as String overrides the field's simple type for this parameter.
  • labels: String[String] declares a dictionary parameter with String values and String keys. The result uses the same dictionary cardinality.

References are resolved in model context. A complete project build performs the required cross-file linking and semantic validation. Parameter cardinality is CMN syntax; a selected facility decides which parameter shapes it can expose in generated code or over a transport.

6. Result cardinality

Operation results use the same cardinality notation as fields:

cmn
package com.example.results

import org.joinedworkz.facilities.common.base

platform Base

type Result {
    value: String
}

service ResultService {
    optionalResult(): Result
    requiredResult(): Result*
    boundedResults(): Result[1..10]
    resultsByName(): Result[String]
}

See Complex types and fields for the exact bounds and the ValueType[KeyType] orientation. No : means that the operation declares no typed result.

7. Core syntax and facility semantics

The service, operation, parameter, result, with, uses, extends, and augments slots are CMN syntax. Stereotype names, properties, executable statement languages, and generated contracts are profile/facility concerns.

There is currently no canonical stable public example dedicated to service generation. Consequently, this page documents the CMN syntax and Core Model relationships, but makes no general promise about generated service code. Resource-driven SpringBoot APIs are covered in Build a CRUD backend.