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:
- a header – package, layer, imports, platform selection
- 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:
package com.example.main
import org.joinedworkz.facilities.common.base as base
import org.joinedworkz.facilities.common.base.api
import org.joinedworkz.facilities.profiles.springboot
platform SpringBoot exclude DtoCartridge1.1 Package and layer
Project CMN files should normally declare a package. A declared package:
- defines the namespace of the elements declared in the file,
- is part of the fully qualified name of each element,
- drives namespaces, names and output paths (e.g. Java packages).
Later, models can optionally specify a layer in addition to the package. Layers are used by platforms and generators to apply different rules to different parts of the model. In release 1.3.80, tag-aware Java class generators also pass the layer as a source tag so that a matching directory mapping can be selected within their outlet. This routing capability is generator-specific, not a promise for every outlet.
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:
import org.joinedworkz.facilities.common.base.apiYou can also define an alias for the imported package:
import org.joinedworkz.facilities.common.base as baseThe alias can then be used as a prefix when referencing elements from that package, using the alias::Name syntax. For example:
type Info {
createdAt: base::Timestamp
}Here:
baseis the alias defined in the import,Timestampis a type defined in the imported Base model,base::Timestampis 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:
platform SpringBootThis tells the generator to use the SpringBoot platform defined in a .profile 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:
platform SpringBoot exclude DtoCartridgeIn this case:
- the
SpringBootplatform 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:
package com.example.main
import org.joinedworkz.facilities.common.base
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 packagecom.example.main.sub1, - elements inside
subpackage sub2.sub3 { ... }belong tocom.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:
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:
- import the package,
- optionally assign an alias,
- use the alias with the
alias::Namesyntax.
Example:
import org.joinedworkz.facilities.common.base as base
type Info {
createdAt: base::Timestamp
}This makes it clear that Timestamp comes from the Base model. If you have multiple imports defining a type with the same name, using aliases avoids ambiguity.
3.3 Fully qualified references
You can also use a fully qualified name directly:
type Info {
createdAt: org.joinedworkz.facilities.common.base.Timestamp
}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.
