Skip to content

File header & packages

A CMN file can declare a root package and layer, imports, and a platform before its nested packages and model elements. Project models should normally use a named package and an explicit platform.

Complete header example

cmn
api package com.example.customer

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

platform Base

subpackage view {
    type CustomerView {
        id*: Id
        name*: Name
    }
}

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

The order is fixed:

  1. optional root package declaration;
  2. zero or more imports;
  3. optional platform declaration;
  4. zero or more nested package blocks;
  5. root-package model elements.

Nested packages cannot be interleaved with root-package model elements.

1. Root package

cmn
package com.example.customer

The package is the technical namespace of the elements declared directly in the model. A qualified package name consists of dot-separated identifiers.

The grammar permits a file without a named root package. This is useful as a container for absolute nested package blocks, but ordinary project models should declare their package explicitly:

cmn
package com.example.customer {
    type Customer {
    }
}

package com.example.order {
    type Order {
    }
}

These two package blocks are absolute: their effective names are com.example.customer and com.example.order.

The grammar also accepts abstract before a root or nested package declaration. JoinedWorkz does not define one universal public generation effect for an abstract package; use it only when the selected facility documents the intended behavior.

Package properties

Properties can follow a package name:

cmn
api package com.example.customer vendorSpecificMimeType=true

The property slot is part of the CMN grammar. vendorSpecificMimeType and its effect are defined by the active platform and generator, not by the package grammar. See Common syntax and the relevant facility reference before adding package properties.

Nested package and subpackage blocks inherit applicable package properties that they do not declare themselves. If the same property occurs at more than one package level, the concrete package wins, followed by its nearest parent and then more distant parents. JoinedWorkz applies this most-specific-first rule consistently; see the upgrade note when migrating models with conflicting parent and subpackage values.

The generic generator defines finalized=true on a root model as an advanced boundary for additional processing through imports. It does not prevent that model from being generated normally with its own Platform. See Finalizing additional import processing.

2. Package uniqueness

Every effective package name must be unique within the model space processed by JoinedWorkz. This applies to root packages in different .cmn files and to nested packages. Do not distribute one package across several model files.

A layer is not part of the package identity and cannot distinguish duplicate package names. During a Maven build, JoinedWorkz checks source and dependency models before generation. A collision stops the build with CMN_DUPLICATE_PACKAGE and reports the conflicting locations; JoinedWorkz does not merge their declarations.

3. Layers

A layer is a free identifier immediately before package:

cmn
api package com.example.customer

There is no layer keyword. Names such as core, api, or backend are ordinary identifiers, not a fixed CMN registry.

The declared layer normally becomes the source tag used for outlet routing. A generator can assign a different source tag to a particular generated output. If it does, outlet lookup uses that generator-assigned tag; it does not retry the lookup with the CMN package layer. Facilities can also give selected layer names semantics beyond routing. These are generator or facility rules and must be documented by that facility.

Relative subpackages inherit the layer of their parent because subpackage has no independent layer position.

4. Relative and absolute nested packages

A relative subpackage uses subpackage:

cmn
package com.example.customer

subpackage api {
    subpackage v1 {
        type CustomerV1 {
        }
    }

    type CustomerView {
    }
}

The effective names are com.example.customer.api and com.example.customer.api.v1. A relative subpackage requires a named parent; otherwise validation reports subpackageWithoutNamedParent.

A nested package declaration remains absolute, even below a named root:

cmn
package com.example.customer

package com.example.shared {
    type SharedValue {
    }
}

SharedValue belongs to com.example.shared, not to com.example.customer.com.example.shared. Imports and the platform are declared on the root model and apply to its nested packages.

5. Imports

Imports expose model elements from another package:

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

The first Base package supplies common types such as String and Id; the second supplies resource method types such as read and create. These names are model elements contributed by the facility, not built-in primitive tokens or CMN keywords.

An import can itself carry properties. The generic generator defines finalized=true to skip additional Cartridge processing through that import; the package remains visible for references. Other import-property meanings must be documented by the selected profile or facility. See Finalizing additional import processing.

Importing a CMN package makes its elements available for references. It does not select a platform; platform selection is a separate declaration.

The imported package remains a separate model with its own namespace, source, validation unit and Platform selection, if present. In an advanced composition, the importing model's Platform can contribute active Cartridge processing without merging the models. See Imported model processing and diagnostics.

6. Aliases and references

An import can define a single-identifier alias:

cmn
import com.example.shared as shared

Use the alias before :::

cmn
type AuditInfo {
    createdAt: shared::Timestamp
}

Without an alias, an unambiguous imported element can be referenced by its simple name. Dotted fully qualified references are supported in grammar positions that accept qualified names:

cmn
type AuditInfo {
    createdAt: com.example.shared.Timestamp
}

Do not place a dotted package name before ::; that prefix position is for an alias. Some specialized grammar positions accept only a simple or alias-qualified reference, so use imports and aliases for portable models.

7. Platform selection

cmn
platform Base

The platform determines which profile context and cartridge applications interpret the current model. Platform selection also defines which stereotypes, properties, outlets, and conventions are available.

Base, Java, and SpringBoot are stable public platforms. Quasar is an experimental public platform. Platform names are profile-defined references, not hard-coded CMN keywords beyond the platform declaration itself.

The grammar permits platform properties after the platform reference. Their names and effects are profile-defined; there is no universal set of CMN platform properties.

Excluding cartridges

One or more cartridges can be excluded for the model:

cmn
platform Base exclude OpenApiCartridge
cmn
platform Base exclude OpenApiCartridge, DiagramCartridge

The cartridge names come from the selected profile. An explicit CMN exclusion takes precedence over project configuration that would otherwise enable that cartridge. Nested packages use the platform and exclusions of their root model.