How to Integrate DDDplus Architecture Enforcement Rules into a CI Pipeline
Integrating DDDplus architecture enforcement rules into a CI pipeline requires configuring the dddplus-maven-plugin to execute ArchUnit-based checks during the Maven verify phase, ensuring that domain-driven design constraints are validated on every build.
The DDDplus framework (available at funkygao/cp-ddd-framework) provides a comprehensive enforcement layer built on ArchUnit that guards against architectural drift in domain-driven design projects. By integrating these rules into your continuous integration pipeline, you automatically prevent violations—such as incorrect layer dependencies or improper service naming—from reaching production code.
Understanding the DDDplus ArchUnit Enforcement Components
The enforcement system consists of three coordinated components that work together to validate your architecture:
- EnforcerMojo: The Maven plugin entry point that bridges the build lifecycle with the enforcement engine.
- DDDPlusEnforcer: The core orchestrator that scans compiled classes and executes rule collections.
- ArchitectureEnforcer: A static repository of predefined ArchUnit rules covering DDDplus-specific conventions.
Step-by-Step: Integrating DDDplus Architecture Enforcement into Your CI Pipeline
Step 1: Configure the Maven Enforcer Plugin
Add the dddplus-maven-plugin to your root pom.xml and bind it to the verify phase. This ensures the check runs after compilation but before packaging.
<build>
<plugins>
<plugin>
<groupId>io.github.dddplus</groupId>
<artifactId>dddplus-maven-plugin</artifactId>
<version>2.1.0</version>
<executions>
<execution>
<id>archunit-enforce</id>
<phase>verify</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rootPackage>com.mycompany.myapp</rootPackage>
<rootDir>target/classes:target/test-classes</rootDir>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
The EnforcerMojo (located at dddplus-maven-plugin/src/main/java/io/github/dddplus/maven/EnforcerMojo.java) reads these parameters and instantiates the enforcement engine.
Step 2: Execute the Enforcer in CI
In your CI configuration, invoke the Maven verify phase. The plugin automatically triggers during this phase.
mvn clean verify -DrootPackage=com.mycompany.myapp -DrootDir=target/classes:target/test-classes
Alternatively, run the goal directly:
mvn io.github.dddplus:dddplus-maven-plugin:enforce \
-DrootPackage=com.mycompany.myapp \
-DrootDir=target/classes:target/test-classes
The DDDPlusEnforcer (defined in dddplus-enforce/src/main/java/io/github/dddplus/DDDPlusEnforcer.java) uses ArchUnit's ClassFileImporter to scan the specified packages, excluding test sources via ImportOption.Predefined.DO_NOT_INCLUDE_TESTS.
Step 3: Configure Build Failure on Violations
The enforcement mechanism relies on ArchUnit's AssertionError throwing capability. When DDDPlusEnforcer.enforce() detects a violation, it propagates the error, causing Maven to exit with a non-zero status.
CI systems interpret this exit code as a failure, blocking the merge or deployment. The detailed violation report appears in the build logs, specifying which rule was broken (e.g., serviceRule(), routerRule(), or optionalInterfaceNameStartsWithI() from ArchitectureEnforcer.java) and the offending class.
Alternative: Direct JUnit Integration for CI
If you prefer test-based enforcement over Maven plugin configuration, instantiate the enforcer directly in a JUnit test. This approach works with any CI system that runs mvn test.
import io.github.dddplus.DDDPlusEnforcer;
import org.junit.Test;
public class ArchitectureComplianceTest {
@Test
public void enforceArchitecture() {
new DDDPlusEnforcer()
.scanPackages("com.mycompany.myapp")
.enforce();
}
}
Place this in src/test/java. The enforce() method throws AssertionError on violation, failing the test and consequently the CI build.
For extended rule sets, use ArchitectureEnforcer.requiredRules:
import io.github.dddplus.ArchitectureEnforcer;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import org.junit.Test;
public class DDDplusArchitectureTest {
@Test
public void requiredArchitectureRules() {
ArchitectureEnforcer.requiredRules.forEach(rule ->
rule.check(new ClassFileImporter().importPackages("com.mycompany.myapp")));
}
}
Key Source Files and Rule Definitions
| File | Purpose | Location |
|---|---|---|
EnforcerMojo.java |
Maven plugin Mojo that parses rootPackage and rootDir, then invokes the enforcer. |
dddplus-maven-plugin/src/main/java/io/github/dddplus/maven/EnforcerMojo.java |
DDDPlusEnforcer.java |
Core API that scans packages via ArchUnit's ClassFileImporter and executes the rule collection. |
dddplus-enforce/src/main/java/io/github/dddplus/DDDPlusEnforcer.java |
ArchitectureEnforcer.java |
Static repository of ArchUnit rules including serviceRule(), routerRule(), and naming conventions. |
dddplus-enforce/src/main/java/io/github/dddplus/ArchitectureEnforcer.java |
DDDPlusEnforcerTest.java |
Reference implementation showing JUnit integration for CI environments. | dddplus-test/src/test/java/io/github/dddplus/DDDPlusEnforcerTest.java |
Summary
- Integrating DDDplus architecture enforcement rules into a CI pipeline involves configuring the
dddplus-maven-pluginto validate ArchUnit rules during the Mavenverifyphase. - The
EnforcerMojobridges Maven with theDDDPlusEnforcer, which scans compiled classes using ArchUnit'sClassFileImporter. - Violations trigger
AssertionError, causing non-zero Maven exit codes that fail CI builds and prevent non-compliant code from merging. - Alternative integration via direct JUnit tests allows
mvn testto serve as the enforcement trigger, utilizingArchitectureEnforcer.requiredRulesfor comprehensive rule coverage.
Frequently Asked Questions
What is the difference between DDDPlusEnforcer and ArchitectureEnforcer?
DDDPlusEnforcer serves as the primary orchestration API that handles package scanning and rule execution. It maintains an internal collection of ArchUnit rules specific to DDDplus core concepts like IIdentityResolver and Router. ArchitectureEnforcer, conversely, is a static utility class that exposes a broader set of architectural constraints—including serviceRule(), routerRule(), and naming conventions like optionalInterfaceNameStartsWithI()—via the requiredRules list. While DDDPlusEnforcer is designed for direct API usage, ArchitectureEnforcer provides reusable rules for custom test implementations.
How do I configure the rootPackage and rootDir parameters?
The rootPackage parameter specifies the base Java package of your application code (e.g., com.mycompany.myapp), which DDDPlusEnforcer.scanPackages() uses to limit ArchUnit's class import scope. The rootDir parameter defines the colon-separated list of compiled class directories (e.g., target/classes:target/test-classes) that the ClassFileImporter scans. In the Maven plugin configuration, these map to the <rootPackage> and <rootDir> XML elements within the EnforcerMojo execution block.
Can I run DDDplus architecture checks without Maven?
Yes, you can execute DDDplus architecture enforcement independently of Maven by using the JUnit integration approach. Instantiate DDDPlusEnforcer directly in a test class, call scanPackages() with your base package, and invoke enforce(). Alternatively, iterate over ArchitectureEnforcer.requiredRules and apply them to classes imported via ArchUnit's ClassFileImporter. This method works with Gradle, Bazel, or standalone JUnit runners, provided the dddplus-enforce dependency is on the classpath.
What happens when an ArchUnit rule violation is detected in CI?
When the DDDPlusEnforcer.enforce() method detects a violation, ArchUnit throws an AssertionError containing a detailed description of which rule was violated (such as serviceRule() or routerRule()) and the specific class or dependency that caused the breach. This error propagates to the Maven process, causing it to exit with a non-zero status code. Consequently, the CI pipeline interprets this as a build failure, halting the workflow and preventing the merge or deployment of the non-compliant code. The detailed violation report appears in the CI logs, allowing developers to identify and fix the architectural drift.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →