This library can help you validating your code against a Context Mapper DSL (CML) model. It enables Context Mapper users to ensure that the implemented code (tactic DDD) corresponds to the CML model with ArchUnit. To make this work, you need to annotate your Java classes with the tactic DDD concepts. Our library supports jMolecules out of the box; but you can use your own set of annotations as well.
You can use this library to write ArchUnit tests by including it into your Gradle or Maven project.
Gradle:
testImplementation 'org.contextmapper:context-mapper-archunit-extension:1.2.0'
Maven:
<dependency>
<groupId>org.contextmapper</groupId>
<artifactId>context-mapper-archunit-extension</artifactId>
<version>1.2.0</version>
</dependency>
Besides the code example below, you can find the complete JavaDoc of the libraries API here.
The simplest way to apply all our test predefined test cases is to extend our AbstractTacticArchUnitTest
. An example:
class TacticArchUnitTestExample extends AbstractTacticArchUnitTest {
@Override
protected String getBoundedContextName() {
return "SampleContext";
}
@Override
protected String getCMLFilePath() {
return "src/test/cml/test.cml";
}
@Override
protected String getJavaPackageName2Test() {
return "org.contextmapper.archunit.sample.sampleaggregate1";
}
}
You basically have to provide three parameters:
- The name of the Bounded Context against you want to test (CML).
- The path to the CML file.
- The package that shall be scanned for the JMolecules annotations.
In case you don't want apply all our rules, you can also implement your custom test cases. An example:
public class ExampleArchitectureTest {
private BoundedContext context;
private JavaClasses classes;
@BeforeEach
protected void setup() {
this.context = new BoundedContextResolver()
.resolveBoundedContextFromModel("src/main/cml/model.cml", "SampleBoundedContext");
this.classes = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("org.example.app");
}
@Test
void aggregatesShouldBeModeledInCML() {
aggregateClassesShouldBeModeledInCml(context).check(classes);
}
@Test
void entitiesShouldBeModeledInCML() {
entityClassesShouldBeModeledInCml(context).check(classes);
}
@Test
void valueObjectsShouldBeModeledInCML() {
valueObjectClassesShouldBeModeledInCml(context).check(classes);
}
}
The three example tests above ensure that Aggregates, Entities, and Value Objects that exist in the Java code, also exist in the CML model. In case a developer adds an Aggregate, Entity, or Value Object to the code that is not part of the CML model, the test will fail.
The example above uses our predefined rules based on the jMolecules annotations. You can find all available rules here. The next section shows how you can write your own rules, in case you don't want to use jMolecules.
By using only our ArchCondition's it is also possible to implement the tests with other annotations than JMolecules:
public class ExampleArchitectureTest {
protected BoundedContext context;
protected JavaClasses classes;
@BeforeEach
protected void setup() {
this.context = new BoundedContextResolver()
.resolveBoundedContextFromModel("src/main/cml/model.cml", "SampleBoundedContext");
this.classes = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("org.example.app");
}
@Test
void aggregatesShouldBeModeledInCML() {
classes().that().areAnnotatedWith(Aggregate.class).should(beModeledAsAggregatesInCML(context));
}
@Test
void entitiesShouldBeModeledInCML() {
classes().that().areAnnotatedWith(Entity.class).should(beModeledAsEntitiesInCML(context));
}
@Test
void valueObjectsShouldBeModeledInCML() {
classes().that().areAnnotatedWith(ValueObject.class).should(beModeledAsValueObjectsInCML(context));
}
}
As the example illustrates, in this case you are able to select your classes with your own code (using your own annotations); but you can still use our conditions, that check specific things in the CML model. You can find all conditions that are currently available here.
Hint: The available rules are implemented with the jMolecules DDD annotations. However, you can implement the same rules with your own set of annotations or interfaces by using the corresponding ArchUnit conditions (as documented above).
Rule / Condition | Description |
---|---|
aggregatesShouldBeModeledInCML |
Aggregates that are implemented in the code (for example annotated with @AggregateRoot jMolecules annotation) shall exist in the CML Bounded Context as well. |
modulesShouldBeModeledInCML |
Modules that are implemented in the code (for example annotated with @Module jMolecules annotation) shall exist in the CML Bounded Context as well. |
entitiesShouldBeModeledInCML |
Entities that are implemented in the code (for example annotated with @Entity jMolecules annotation) shall exist in the CML Bounded Context as well. |
valueObjectsShouldBeModeledInCML |
Value Objects that are implemented in the code (for example annotated with @ValueObject jMolecules annotation) shall exist in the CML Bounded Context as well. |
domainEventsShouldBeModeledInCML |
Domain events that are implemented in the code (for example annotated with @DomainEvent jMolecules annotation) shall exist int the CML Bounded Context as well. |
servicesShouldBeModeledInCML |
Services that are implemented in the code (for example annotated with @Service jMolecules annotation) shall exist in the CML Bounded Context as well. |
repositoriesShouldBeModeledInCML |
Repositories that are implemented in the code (for example annotated with @Repository annotation) shall exist in the CML Bounded Context as well. |
aggregatesShouldAdhereToCmlAggregateStructure |
This rule ensures that an Aggregate in the code (basically a Java package with a marker on the aggregate root entity; for example annotated with @AggregateRoot jMolecules annotation) consists of the same entities, value objects, and domain events as it is modeled in CML. |
entitiesShouldAdhereToCmlEntityStructure |
This rule ensures that the fields of an entity in the code (for example annotated with @Entity jMolecules annotation) are also modeled in the corresponding CML entity. |
valueObjectsShouldAdhereToCmlValueObjectStructure |
This rule ensures that the fields of a value object in the code (for example annotated with @ValueObject jMolecules annotation) are also modeled in the corresponding CML value object. |
domainEventsShouldAdhereToCmlDomainEventStructure |
This rule ensures that the fields of a domain event in the code (for example annotated with @DomainEvent jMolecules annotation) are also modeled in the corresponding CML domain event. |
The rules and conditions are documented in our JavaDoc as well.
Missing some rules/conditions? Contributions are always welcome! Create PRs or GitHub issues with your ideas/requirements.
Contribution is always welcome! Here are some ways how you can contribute:
- Create Github issues if you find bugs or just want to give suggestions for improvements.
- This is an open source project: if you want to code, create pull requests from forks of this repository. Please refer to a Github issue if you contribute this way.
- If you want to contribute to our documentation and user guides on our website https://contextmapper.org/, create pull requests from forks of the corresponding page repo https://github.com/ContextMapper/contextmapper.github.io or create issues there.
ContextMapper is released under the Apache License, Version 2.0.