Skip to content

Create a project with Maven

This page explains how to set up a JoinedWorkz-enabled project with Maven.

It goes deeper than the Quickstart:

  • the Quickstart shows a minimal, opinionated example;
  • this page describes the general Maven setup that you can adapt for real projects and multi-module builds.

Prerequisites

Before you start, make sure you have:

  • Java 17 or newer
  • Maven 3.8+ on your PATH
  • access to the JoinedWorkz Maven artifacts (for example via the public repository or your organisation's repository manager)

If you have not done so yet, you may want to walk through the Quickstart once to get a feeling for the overall flow (model → Maven build → generated artifacts).


1. Minimal Maven setup

The smallest useful Maven setup looks like this:

  • a project pom.xml with
    • the JoinedWorkz Maven plugin,
    • the common-base dependency,
    • your normal Java build configuration;
  • a model directory that contains your .cmn model files,
  • an optional joinedworkz.properties file in the project root for outlet overrides and other generator configuration.

1.1 pom.xml template

The following pom.xml is a good starting point for a simple project:

xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>joinedworkz-quickstart</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <joinedworkz.version>1.3.74</joinedworkz.version>
    </properties>

    <build>
        <resources>
            <!-- model files are treated as resources so they are available on the classpath -->
            <resource>
                <directory>model</directory>
            </resource>
            <!-- generated artifacts (e.g. OpenAPI) are also added as resources -->
            <resource>
                <directory>src/generated/resources</directory>
            </resource>
        </resources>

        <plugins>
            <!-- JoinedWorkz generator plugin -->
            <plugin>
                <groupId>org.joinedworkz.cmn</groupId>
                <artifactId>cmn-maven-plugin</artifactId>
                <version>${joinedworkz.version}</version>
                <executions>
                    <execution>
                        <?m2e ignore?>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

    </build>

    <dependencies>
        <!-- modeling and generation tools (provided) -->
        <dependency>
            <groupId>org.joinedworkz.facilities</groupId>
            <artifactId>common-base</artifactId>
            <version>${joinedworkz.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

Key points:

  • The JoinedWorkz generator plugin (cmn-maven-plugin) contains the parser, model transformation and orchestration of generators.
  • The common-base dependency contributes shared models and runtime needed by your own .cmn models.
  • Additional platforms and generators are simply added as further dependencies; the plugin will pick them up at runtime and execute the cartridges configured by your platform definitions.

1.2 Project layout

A typical layout for a simple project is:

text
pom.xml
joinedworkz.properties
model/
  quickstart-model.cmn
src/
  main/
    java/              # your handwritten Java code
    resources/         # additional handwritten resources
  generated/
    resources/         # generated artifacts (e.g. OpenAPI)

You can choose different directories if you prefer; just make sure that:

  • the model directory in the POM matches where you place your .cmn files,
  • any directories with generated resources or sources are registered as Maven resource / source directories if you want to include them in your build artifacts,
  • the configuration file joinedworkz.properties is located in the project root, so that it is visible to the project classloader.

2. Adding JoinedWorkz to an existing Maven project

To add JoinedWorkz to an existing Maven project:

  1. Add the joinedworkz.version property (or reuse your own version management mechanism).
  2. Add the cmn-maven-plugin to the <build><plugins> section.
  3. Add common-base and any platform/generator dependencies that you need.
  4. Create a model directory and put your .cmn files there.
  5. Optionally, register src/generated/resources (or your preferred output directory) as a Maven resource.
  6. Add a joinedworkz.properties file in the project root if you want to override outlets or pass additional configuration to cartridges and generators.

After that you can run:

bash
mvn clean package

JoinedWorkz will:

  • load the models from the model directory,
  • transform them,
  • determine the referenced platforms,
  • load joinedworkz.properties via the project classloader (if present),
  • and execute the corresponding cartridges and generators.

3. Multi-module projects

Many real-world systems are built as multi-module Maven projects. You can use JoinedWorkz in different ways in such setups.

3.1 Model in a dedicated module

One common pattern is to have a dedicated model module:

text
parent-pom/
  pom.xml
  model/
    pom.xml
    joinedworkz.properties   # outlet overrides for this module
    model/                   # CMN models
  service-api/
    pom.xml                  # consumes generated OpenAPI or DTOs
  service-impl/
    pom.xml                  # generated + handwritten implementation

In this pattern:

  • the model module contains the JoinedWorkz plugin, the joinedworkz.properties file and all model files;
  • the generators produce artifacts (for example OpenAPI or DTO JARs);
  • other modules depend on those artifacts.

Benefits:

  • clear separation between model and implementation;
  • the model module can be reused in other projects.

3.2 Model and implementation in the same module

For smaller projects it is perfectly fine to keep everything in a single module, like in the Quickstart:

  • the model drives generation of OpenAPI and/or code,
  • handwritten code lives alongside the generated code/resources,
  • the module produces a single deployable artifact.

Choose the structure that best fits your build and team.

3.3 Model module with outlet overrides (no module dependencies)

In some cases you do not want other modules to depend directly on the model module. Instead, you keep all models in a dedicated module but let the generators write their outputs into sibling modules.

This is achieved by overriding outlet directories via properties in the joinedworkz.properties file of the model module.

Consider a parent POM with modules:

text
parent-pom/
  pom.xml            # aggregator
  model/
    pom.xml
    joinedworkz.properties
    model/           # CMN models
  my-commons-module/
    pom.xml          # uses generated Java sources
  my-service-module/
    pom.xml          # uses generated Java sources
  my-webapp-module/
    pom.xml          # uses generated Java sources

The model module does not appear as a Maven dependency of the other modules. To make sure generation runs before the modules that consume the generated artifacts, list the modules in the parent POM in the correct order:

xml
<modules>
    <module>model</module>
    <module>my-commons-module</module>
    <module>my-service-module</module>
    <module>my-webapp-module</module>
</modules>

Maven respects this order when there are no dependency relationships that override it, so the model module is built first.

The actual target directories for generated artifacts are then controlled by outlet override properties in joinedworkz.properties (see the next section).


4. Outlet overrides and joinedworkz.properties

Each cartridge defines one or more outlets, for example an outlet for generated Java sources. The cartridge provides default paths for these outlets, but you can override them via properties.

Outlet overrides and other generator configuration are defined in a joinedworkz.properties file in the root folder of the project (or in the root of the model module in multi-module setups).

At build time the generator loads this file via the project classloader and passes the properties to cartridges and generators.

4.1 Global overrides

A global override changes the directory for an outlet for all model packages:

properties
# joinedworkz.properties
# common outlet overrides
outlet.generatedJavaSource.directory=../my-service-module/src/generated/java

In this example, the outlet generatedJavaSource of the Java cartridge is configured to write its output into the src/generated/java directory of the sibling module my-service-module.

4.2 Layer-specific overrides

Models can be organised into layers. Conceptually, a layer is assigned to a model package and can be used to route generated artifacts differently per layer.

For example, you might have layers such as commons and webapp and want their generated Java code to end up in different modules. You can express this with layer-specific overrides in joinedworkz.properties:

properties
# joinedworkz.properties
# specific outlet overrides (for model packages with layer: commons, webapp)
outlet.generatedJavaSource.commons.directory=../my-commons-module/src/generated/java
outlet.generatedJavaSource.webapp.directory=../my-webapp-module/src/generated/java

Here:

  • model packages that belong to the commons layer generate Java sources into my-commons-module/src/generated/java,
  • model packages in the webapp layer generate into my-webapp-module/src/generated/java.

Layer-specific overrides take precedence over the general outlet override for that outlet.

In addition to outlet directories, joinedworkz.properties can also contain other override properties, for example to adjust generated Java package names or cartridge-specific options.

The concept of layers and how they are defined in CMN models is described in more detail in the Modeling section. For the purpose of Maven setup it is enough to know that you can direct generated output per layer using the <outlet>.<layer>.directory properties.


5. Customising the plugin

The minimal configuration uses the default behaviour of the cmn-maven-plugin. For more advanced setups you can:

  • configure different output directories for specific outlets (globally or per layer, as shown above),
  • adjust the execution phase (e.g. run generation in generate-sources instead of the default phase),
  • pass additional configuration parameters understood by your platforms and cartridges.

These options are defined by the plugin and the specific platforms you use. Refer to the Maven plugin reference and cartridge documentation for the complete list of configuration options and supported properties.

Tip: Keep the configuration in your pom.xml as small as possible and move platform-specific details into platform profile files (.profil) and joinedworkz.properties, so that you can reuse the same model with different platforms and project structures.


6. Next steps

Once your Maven project is set up, continue with:

  • Quickstart for a concrete end-to-end example.
  • the Modeling section for details on CMN syntax, layers and platforms.
  • the How-to guides for tasks like implementing a custom generator or integrating JoinedWorkz into an existing Java system.