Skip to content

Model file structure (.cmn)

This page explains how a Canonical Model Notation (CMN) model file is structured and how elements in different files reference each other.

A CMN file has two main parts:

  1. a header – package, layer, imports, platform selection
  2. the content – types, services, resources, components, applications, …

Understanding this structure helps you organise larger models and reuse elements across packages and modules.


1. Header

The header appears at the top of a .cmn file and typically contains:

  • the package (and optional layer)
  • one or more imports (with optional aliases)
  • the platform specification for the model

A simplified example:

text
package com.example.main

import com.example.core as core
import org.joinedworkz.facilities.common.base.api

platform SpringBoot exclude DtoCartridge

1.1 Package and layer

Every CMN file belongs to a package. The package:

  • defines the namespace of the elements declared in the file,
  • is part of the fully qualified name of each element,
  • is used by platforms for naming (e.g. Java packages) and outlet routing.

Later, models can optionally specify a layer in addition to the package. Layers are used by platforms and cartridges to route generated artifacts (for example via outlet overrides) and apply different rules to different parts of the model. Layers are explained in more detail in the Modeling section on layers and outlet routing.

1.2 Imports and aliases

Imports make elements from other packages available in the current file without having to use fully qualified names everywhere.

A basic import looks like:

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

You can also define an alias for the imported package:

text
import com.example.core as core

The alias can then be used as a prefix when referencing elements from that package, using the alias::Name syntax. For example:

text
type Info {
    createdAt: core::DateTime
}

Here:

  • core is the alias defined in the import,
  • DateTime is a type defined in the com.example.core package,
  • core::DateTime is an explicit reference to that type.

This has a few advantages:

  • your model stays readable even with longer package names,
  • you can clearly see from which package a referenced element comes,
  • you avoid ambiguities when different packages define elements with the same simple name.

If you do not use an alias, you can either rely on simple names (if there is no ambiguity) or use fully qualified names where supported by the DSL.

1.3 Platform specification

The platform specification selects the platform that should be used for this model file (or for the package/module it belongs to).

A simple platform declaration looks like:

text
platform SpringBoot

This tells the generator to use the SpringBoot platform defined in a .profil file. The platform controls which cartridges and generators are applied to the model and how they interpret its elements.

You can also exclude specific cartridges from the platform for this model, for example:

text
platform SpringBoot exclude DtoCartridge

In this case:

  • the SpringBoot platform is still used,
  • all its normal cartridges apply,
  • except the cartridge named DtoCartridge, which is disabled for this model.

This allows you to fine-tune generation behaviour without changing the platform profile itself. Typical use cases include:

  • disabling a cartridge for experimental or partial migrations,
  • avoiding generation of certain artifacts for specific modules,
  • temporarily turning off a cartridge while adjusting its configuration.

2. Content: model elements

After the header, a CMN file contains the model elements that make up your canonical model. The main categories are:

  • data types – simple, complex types and enums
  • services – operations representing business capabilities
  • resources – externally visible APIs (e.g. REST endpoints)
  • components – group resources/services into deployable units
  • applications – top-level compositions of components

These elements are described conceptually in:

Within the same file, you can define multiple elements of each kind. All of them share the package from the header.

2.1 Subpackages

To structure larger models, you can define subpackages inside a package using the subpackage keyword. For example:

text
package com.example.main

import ...

platform SpringBoot

subpackage sub1 {

    // model elements of com.example.main.sub1
}

subpackage sub2.sub3 {

    // model elements of com.example.main.sub2.sub3
}

In this example:

  • elements inside subpackage sub1 { ... } belong to the package com.example.main.sub1,
  • elements inside subpackage sub2.sub3 { ... } belong to com.example.main.sub2.sub3.

Subpackages help you:

  • group related model elements without splitting them into many files,
  • reflect a logical hierarchy inside a larger package,
  • keep the header (imports, platform) in one place while structuring the content.

Each subpackage shares the imports and platform specification of the parent package; you do not need to repeat them inside the subpackage block.


3. Referencing model elements

CMN models frequently reference elements defined:

  • in the same file,
  • in other files of the same package,
  • in other packages (via imports and aliases).

3.1 Same package

Within the same package, you normally reference elements by their simple name, for example:

text
type Greeting {
    message: String
}

resource /hello as Greeting {
    create()
    read()
}

Here, Greeting is a type defined in the same package, so no prefix or qualification is needed.

3.2 Imported packages and aliases

When referencing elements from imported packages, you typically:

  1. import the package,
  2. optionally assign an alias,
  3. use the alias with the alias::Name syntax.

Example:

text
import com.example.core as core

type Info {
    createdAt: core::DateTime
}

This makes it clear that DateTime comes from com.example.core. If you have multiple imports defining a type called DateTime, using aliases avoids ambiguity.

3.3 Fully qualified references

Depending on the exact DSL rules, you may also be able to use fully qualified names directly for some references, for example:

text
type Info {
    createdAt: com.example.core::DateTime
}

In practice, aliases are usually preferred for readability, especially when the same external package is used in many places.


4. Summary

A CMN model file is structured into:

  • a header, which defines:
    • the package (and optional layer),
    • imports and aliases,
    • the platform and optional cartridge exclusions;
  • a content section with the actual model elements:
    • types, services, resources, components, applications,
    • optionally organised into subpackages.

References between elements rely on:

  • simple names within the same package,
  • imports and aliases for other packages (alias::Name),
  • and, where appropriate, fully qualified names.

Keeping this structure in mind makes it easier to:

  • split models across multiple files and packages,
  • share common types via imported base models,
  • control platform behaviour per package or module,
  • and maintain large models over time.