Skip to content

Integrate into an existing Java project

This guide shows how to add JoinedWorkz step by step to an existing Java project, without rewriting everything from scratch.

Typical goals are:

  • generate OpenAPI for existing or planned REST APIs
  • generate DTOs / API interfaces and use them from hand-written code
  • gradually move parts of the API and domain model into CMN models

We’ll focus on Maven-based projects. The examples assume you use Java 21 and, for REST, a Spring Boot stack – but the patterns apply to other Java technologies as well.

For background reading see:

This guide requires Java 21 and Maven 3.9+. It describes an adoption pattern rather than a complete example project. Use the linked Quickstart, Spring Boot example and multi-module guide for complete configurations.


1. Integration strategies

There is no single “right” way to introduce JoinedWorkz into an existing project. In practice, three patterns work well:

  1. Model inside an existing module
    Put CMN models directly into an existing Maven module (for example the REST API module) and generate OpenAPI and DTOs alongside your current code.

  2. Dedicated model module (recommended for larger systems)
    Create a new Maven module that only contains CMN models and the JoinedWorkz plugin, and route generated artifacts into existing modules via joinedworkz.properties.

  3. Hybrid approach
    Start with (1) inside an existing module, and later extract the models into a dedicated module when the model grows.

This guide will show the first two approaches, so you can choose what fits your project best.


2. Prerequisites

Your existing project should:

  • be built with Maven
  • use Java 21
  • use Maven 3.9 or newer
  • have a module where it makes sense to introduce API/domain modeling (e.g. a “web”, “api”, “service” or “backend” module)

You also need:

  • JoinedWorkz Studio installed (optional but highly recommended)
  • access to Maven Central (for the JoinedWorkz facilities and plugin)

The basic dependencies are:

xml
<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <joinedworkz.version>1.3.81</joinedworkz.version>
</properties>

<dependencies>
    <!-- Spring Boot platform (includes Java and Base via transitive dependencies) -->
    <dependency>
        <groupId>org.joinedworkz.facilities</groupId>
        <artifactId>spring-boot</artifactId>
        <version>${joinedworkz.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

You can also depend only on common-base or common-java if you do not use Spring Boot, see Facilities & platforms.


3. Option A – Integrate into an existing module

This is the simplest way to start. You:

  • add the JoinedWorkz plugin to an existing Maven module
  • add a model/ folder with CMN models
  • register generated resources/sources if needed

3.1 Add the plugin and facilities

In the existing module where you want to introduce modeling (for example your REST API module), update the POM:

xml
<build>
    <resources>
        <!-- existing resources -->
        <resource>
            <directory>src/main/resources</directory>
        </resource>

        <!-- CMN models -->
        <resource>
            <directory>model</directory>
        </resource>

        <!-- generated resources (e.g. OpenAPI) -->
        <resource>
            <directory>src/generated/resources</directory>
        </resource>
    </resources>

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

        <!-- register generated Java sources (optional, if you generate Java) -->
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <version>3.6.0</version>
            <executions>
                <execution>
                    <id>add-generated-source</id>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>add-source</goal>
                    </goals>
                    <configuration>
                        <sources>
                            <source>${basedir}/src/generated/java</source>
                        </sources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Add the Spring Boot facility (or the facility you need) as a dependency:

xml
<dependencies>
    <!-- JoinedWorkz facilities (SpringBoot, brings in Java + Base) -->
    <dependency>
        <groupId>org.joinedworkz.facilities</groupId>
        <artifactId>spring-boot</artifactId>
        <version>${joinedworkz.version}</version>
        <scope>provided</scope>
    </dependency>

    <!-- your existing dependencies -->
    ...
</dependencies>

3.2 Add a first CMN model

Create a model/ directory next to src/main/java and src/main/resources and add a CMN file, for example:

  • model/customer-api.cmn
cmn
api package com.example.existing.api

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

platform SpringBoot

type<entity> Customer {
    id**:       Id
    firstName*: Name
    lastName*:  Name
    email:      String(255)
}

resource /customers as Customer[] by id {

    queryEntities()
    createEntity()
    readEntity()
    updateEntity()
    deleteEntity()
}

You can now:

  • run mvn clean package
  • or open the project in JoinedWorkz Studio and generate from there

With the default outlets from Base and SpringBoot, inspect these configured output directories:

  • src/generated/resources/openapi for model-scoped OpenAPI;
  • diagram/api for matching OpenAPI viewers;
  • src/generated/java for Java output selected by the platform.

The exact file names derive from the effective model and component namespaces. This illustrative integration fragment is not assigned invented expected file names. See the Spring Boot example for release-built, exact paths.

The resource-only model above produces the model-scoped OpenAPI document. With SpringBoot, add a component with provide /customers before expecting a generated API interface or controller. That component also produces the additional component-scoped OpenAPI aggregate.

3.3 Use generated artifacts from existing code

How you integrate the generated artifacts depends on what you generate:

  • OpenAPI
    Publish the generated YAML/HTML as your derived API documentation contract, or feed the OpenAPI into your existing tooling (e.g. client generation). The CMN model remains the authoritative input for this replaceable output.

  • DTOs Let JoinedWorkz generate DTOs and reference them from your hand-written controllers or services instead of writing DTOs by hand.

  • API interfaces and controllers Define a component that provides the resource. SpringBoot generates the API interface and controller from that provide boundary, not from a resource model alone. Treat the generated files as replaceable output. Keep business logic in project-owned services outside src/generated/**; do not adopt generated controller files as manually maintained sources. The canonical release example calls its generated CustomerDataAccessService directly and does not demonstrate a manual handler layer.

    See also Spring Boot facility

A common pattern is:

  1. generate DTOs from CMN,
  2. add a component provide boundary when you want a generated API interface/controller and component OpenAPI aggregate,
  3. keep manual business logic behind the generated boundary,
  4. let the CMN model and facilities handle OpenAPI and documentation.

4. Option B – Add a dedicated model module

For larger projects or when you want to keep modeling concerns separate, create a dedicated model module and route outputs into existing modules.

4.1 Create a model module

Add a new Maven module (for example model) to your multi-module project:

xml
<modules>
    <module>model</module>
    <module>backend</module>
    <module>webapp</module>
    ...
</modules>

In model/pom.xml:

xml
<project>
    ...
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <joinedworkz.version>1.3.81</joinedworkz.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.joinedworkz.facilities</groupId>
            <artifactId>spring-boot</artifactId>
            <version>${joinedworkz.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>model</directory>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.joinedworkz.cmn</groupId>
                <artifactId>cmn-maven-plugin</artifactId>
                <version>${joinedworkz.version}</version>
                <executions>
                    <execution>
                        <?m2e ignore?><!-- ignore this execution in Eclipse -->
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

In this module you only keep:

  • CMN models (e.g. domain, API, backend components)
  • no hand-written Java code

4.2 Route outputs with layers and facility-specific outlets

Use joinedworkz.properties in the root of the model module (or project) to route generated outputs into existing modules. A layer such as core normally becomes the effective layer for generated output and can select a layer-specific directory. A generator can assign a different effective layer to a particular output. Every mapping still applies to one exact facility outlet.

Example for a project with:

  • backend module (services, domain, persistence)
  • webapp module (Spring Boot application, controllers, HTTP boundary)

model/joinedworkz.properties:

properties
# default for generated Java sources
outlet.generatedJavaSource.directory=src/generated/java

# domain-related Java code (core layer) to backend module
outlet.generatedJavaSource.core.directory=../backend/src/generated/java

# SpringBoot controller/API output (effective layer api) to web module
outlet.generatedJavaSource.api.directory=../webapp/src/generated/java

# OpenAPI and HTML viewers globally to the web module
outlet.generatedOpenApi.directory=../webapp/src/generated/resources/openapi
outlet.generatedOpenApiHtml.directory=../webapp/diagram/api

The exact outlet names depend on the platform (see the Base and SpringBoot profiles):

  • outlet.<outletName>.directory – default directory
  • outlet.<outletName>.<layer>.directory – mapping for one exact effective layer

Generated output normally retains the CMN model layer. SpringBoot controller and API-interface output is explicitly assigned effective layer api, even when the component model has no declared layer. This example defines no layer-specific OpenAPI mappings, so its global OpenAPI routes collect documents from every effective layer. A matching layer-specific mapping would take precedence. Make sure the target modules (backend, webapp, …) register these directories as resources or sources as described in the Maven plugin reference.

4.3 Build order in the parent POM

In a multi-module setup, ensure that the model module is built first so that generated artifacts are available when compiling dependent modules.

In the parent pom.xml:

xml
<modules>
    <module>model</module>
    <module>backend</module>
    <module>webapp</module>
</modules>

For a sequential reactor build, this order runs the model module before the consumer modules. Do not rely on <modules> order as synchronization with mvn -T: Maven may compile otherwise independent consumers while generation is still running. Use a sequential build for filesystem-based cross-module output, or establish real Maven dependencies that order the consumers after the model module.


5. Gradual adoption patterns

You do not need to model the whole existing system at once. Common gradual steps are:

  1. OpenAPI only
    Start by modeling just the REST resources in CMN and generate OpenAPI and diagrams. Keep all implementation code unchanged.

  2. Introduce DTOs
    Add CMN types (entities, projections) and let JoinedWorkz generate DTOs. Use the generated DTOs in your existing controllers/services instead of hand-written ones.

  3. Introduce component / application models
    Use a component to combine all endpoints in its provide declarations into an additional component-scoped OpenAPI document, even when the resources come from several imported CMN models. Applications add the higher-level diagrams; JoinedWorkz does not create another OpenAPI aggregate per application.

  4. Optional: Generated API interface and controller

    When you are comfortable with the model, let the SpringBoot platform generate an API interface and controller from the component's provide boundary. Keep additional manual business services outside the replaceable generated tree.

Classify every introduced output with the ownership matrix. In particular, do not turn a replaceable controller into a manual source without an explicit ownership transition.

Throughout this process, your existing Java code remains in control of the runtime behavior. JoinedWorkz augments the project with generated artifacts that you can adopt step by step.


6. Summary

To integrate JoinedWorkz into an existing Java project:

  1. Decide on a strategy

    • integrate into an existing module, or
    • create a dedicated model module and route outputs via outlets.
  2. Add the facilities and Maven plugin
    Include the appropriate facility (Base, Java, SpringBoot, …) and configure the cmn-maven-plugin as described above.

  3. Create CMN models
    Start small – for example with one API and its key types – and grow the model over time.

  4. Configure resources and generated sources
    Register generated resource/source directories in the modules that use them.

  5. Use the generated artifacts Wire OpenAPI, DTOs and (optionally) generated API/controller boundaries into your existing project, gradually replacing hand-written boilerplate where it makes sense.

For concrete CMN examples, see: