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:
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.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 viajoinedworkz.properties.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:
<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:
<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:
<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
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/openapifor model-scoped OpenAPI;diagram/apifor matching OpenAPI viewers;src/generated/javafor 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
provideboundary, not from a resource model alone. Treat the generated files as replaceable output. Keep business logic in project-owned services outsidesrc/generated/**; do not adopt generated controller files as manually maintained sources. The canonical release example calls its generatedCustomerDataAccessServicedirectly and does not demonstrate a manual handler layer.See also Spring Boot facility
A common pattern is:
- generate DTOs from CMN,
- add a component
provideboundary when you want a generated API interface/controller and component OpenAPI aggregate, - keep manual business logic behind the generated boundary,
- 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:
<modules>
<module>model</module>
<module>backend</module>
<module>webapp</module>
...
</modules>In model/pom.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:
backendmodule (services, domain, persistence)webappmodule (Spring Boot application, controllers, HTTP boundary)
model/joinedworkz.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/apiThe exact outlet names depend on the platform (see the Base and SpringBoot profiles):
outlet.<outletName>.directory– default directoryoutlet.<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:
<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:
OpenAPI only
Start by modeling just the REST resources in CMN and generate OpenAPI and diagrams. Keep all implementation code unchanged.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.Introduce component / application models
Use a component to combine all endpoints in itsprovidedeclarations 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.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
provideboundary. 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:
Decide on a strategy
- integrate into an existing module, or
- create a dedicated model module and route outputs via outlets.
Add the facilities and Maven plugin
Include the appropriate facility (Base, Java, SpringBoot, …) and configure thecmn-maven-pluginas described above.Create CMN models
Start small – for example with one API and its key types – and grow the model over time.Configure resources and generated sources
Register generated resource/source directories in the modules that use them.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:
- Model a REST API step by step
- the self-contained Quickstart
- the Examples page, which identifies the release-compatible source for each example role
