Skip to content

Commit 93e58e3

Browse files
authored
Check correspondence of entities, value objects, and domain events fields (#3)
1 parent ea4257a commit 93e58e3

File tree

27 files changed

+683
-35
lines changed

27 files changed

+683
-35
lines changed

README.md

+19-26
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
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](https://www.archunit.org/). To make this work, you need to annotate your Java classes with the tactic DDD concepts. Our library supports [jMolecules](https://github.com/xmolecules/jmolecules) out of the box; but you can use your own set of annotations as well.
66

7-
**Hint: This is work in progress and in PoC state!** (we are currently evaluating which concrete rules we shall implement first; input very welcome!)
8-
97
## Usage
108
You can use this library to write ArchUnit tests by including it into your Gradle or Maven project.
119

@@ -127,30 +125,25 @@ public class ExampleArchitectureTest {
127125
}
128126
```
129127

130-
## Available Rules (just a PoC for now ;)
131-
| | Description |
132-
|-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
133-
| `aggregatesShouldBeModeledInCML` | Aggregates that are implemented in the code (for example annotated with @AggregateRoot jMolecules annotation) shall exist in the CML Bounded Context as well. |
134-
| `modulesShouldBeModeledInCML` | Modules that are implemented in the code (for example annotated with @Module jMolecules annotation) shall exist in the CML Bounded Context as well. |
135-
| `entitiesShouldBeModeledInCML` | Entities that are implemented in the code (for example annotated with @Entity jMolecules annotation) shall exist in the CML Bounded Context as well. |
136-
| `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. |
137-
| `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. |
138-
| `servicesShouldBeModeledInCML` | Services that are implemented in the code (for example annotated with @Service jMolecules annotation) shall exist in the CML Bounded Context as well. |
139-
| `repositoriesShouldBeModeledInCML` | Repositories that are implemented in the code (for example annotated with @Repository annotation) shall exist in the CML Bounded Context as well. |
140-
| `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. |
141-
| | |
142-
143-
## Ideas for rules to implement
144-
The following list states some rules that could be implemented. We are still working on this list and input it very welcome!
145-
146-
| Rule | Description |
147-
|-----------------------------------------|---------------------------------------------------------------------------------------------------------------------|
148-
| `entityShouldAdhereToCmlStructure` | Structural check of entity: same attributes, same methods, same references |
149-
| `valueObjectShouldAdhereToCmlStructure` | Structure check of value object: same attributes, same methods, same references |
150-
| `{}ShouldAdhereToCmlStructure` | dito. for other tactic DDD objects (domain events, service, etc.) |
151-
| | |
152-
| | |
153-
| | |
128+
## Available Rules and Conditions
129+
_Hint:_ The available rules are implemented with the jMolecules DDD annotations. However, you can implement the same rules with your
130+
own set of annotations or interfaces by using the corresponding ArchUnit conditions.
131+
132+
| Rule / Condition | Description |
133+
|-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
134+
| `aggregatesShouldBeModeledInCML` | Aggregates that are implemented in the code (for example annotated with @AggregateRoot jMolecules annotation) shall exist in the CML Bounded Context as well. |
135+
| `modulesShouldBeModeledInCML` | Modules that are implemented in the code (for example annotated with @Module jMolecules annotation) shall exist in the CML Bounded Context as well. |
136+
| `entitiesShouldBeModeledInCML` | Entities that are implemented in the code (for example annotated with @Entity jMolecules annotation) shall exist in the CML Bounded Context as well. |
137+
| `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. |
138+
| `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. |
139+
| `servicesShouldBeModeledInCML` | Services that are implemented in the code (for example annotated with @Service jMolecules annotation) shall exist in the CML Bounded Context as well. |
140+
| `repositoriesShouldBeModeledInCML` | Repositories that are implemented in the code (for example annotated with @Repository annotation) shall exist in the CML Bounded Context as well. |
141+
| `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. |
142+
| `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. |
143+
| `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. |
144+
| `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. |
145+
146+
**Missing some rules/conditions?** Contributions are always welcome! Create PRs or GitHub issues with your ideas/requirements.
154147

155148
## Contributing
156149
Contribution is always welcome! Here are some ways how you can contribute:

src/main/java/org/contextmapper/archunit/AbstractTacticArchUnitTest.java

+16
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,20 @@ void servicesShouldBeModeledInCML() {
105105
void repositoriesShouldBeModeledInCML() {
106106
repositoryClassesShouldBeModeledInCml(context).check(classes);
107107
}
108+
109+
@Test
110+
void entitiesShouldAdhereToCmlStructure() {
111+
entitiesShouldAdhereToCmlEntityStructure(context).check(classes);
112+
}
113+
114+
@Test
115+
void valueObjectsShouldAdhereToCmlStructure() {
116+
valueObjectsShouldAdhereToCmlValueObjectStructure(context).check(classes);
117+
}
118+
119+
@Test
120+
void domainEventsShouldAdhereToCmlStructure() {
121+
domainEventsShouldAdhereToCmlDomainEventStructure(context).check(classes);
122+
}
123+
108124
}

src/main/java/org/contextmapper/archunit/ContextMapperArchConditions.java

+88
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,125 @@
2121

2222
public class ContextMapperArchConditions {
2323

24+
private ContextMapperArchConditions() {
25+
}
26+
27+
/**
28+
* Checks whether a class, given by its simple name, is represented as an Aggregate in CML.
29+
*
30+
* @param cmlContext the CML Bounded Context within which the check searches for the Aggregates
31+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
32+
*/
2433
public static ModeledAsAggregateInContext beModeledAsAggregatesInCML(BoundedContext cmlContext) {
2534
return ModeledAsAggregateInContext.beModeledAsAggregatesInCML(cmlContext);
2635
}
2736

37+
/**
38+
* Checks whether a class, given by its simple name, is represented as an Entity in CML.
39+
*
40+
* @param cmlContext the CML Bounded Context within which the check searches for the Entities
41+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
42+
*/
2843
public static ModeledAsEntityInContext beModeledAsEntityInCML(BoundedContext cmlContext) {
2944
return ModeledAsEntityInContext.beModeledAsEntityInCML(cmlContext);
3045
}
3146

47+
/**
48+
* Checks whether a class, given by its simple name, is represented as a Value Object in CML.
49+
*
50+
* @param cmlContext the CML Bounded Context within which the check searches for the Value Objects
51+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
52+
*/
3253
public static ModeledAsValueObjectInContext beModeledAsValueObjectInCML(BoundedContext cmlContext) {
3354
return ModeledAsValueObjectInContext.beModeledAsValueObjectInCML(cmlContext);
3455
}
3556

57+
/**
58+
* Checks whether a module (a package-info.java class with an @Module jMolecules annotation that sets the module name)
59+
* is represented as a corresponding module in CML.
60+
* <p>
61+
* Limitation: this rule works with the jMolecules annotation only!
62+
*
63+
* @param cmlContext the CML Bounded Context within which the check searches for the Module
64+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
65+
*/
3666
public static ModeledAsModuleInContext beModeledAsModulesInCML(BoundedContext cmlContext) {
3767
return ModeledAsModuleInContext.beModeledAsModulesInCML(cmlContext);
3868
}
3969

70+
/**
71+
* Checks whether a class, given by its simple name, is represented as a Domain Event in CML.
72+
*
73+
* @param cmlContext the CML Bounded Context within which the check searches for the Domain Events
74+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
75+
*/
4076
public static ModeledAsDomainEventInContext beModeledAsDomainEventInCML(BoundedContext cmlContext) {
4177
return ModeledAsDomainEventInContext.beModeledAsDomainEventInCML(cmlContext);
4278
}
4379

80+
/**
81+
* Checks whether a class, given by its simple name, is represented as a service in CML.
82+
*
83+
* @param cmlContext the CML Bounded Context within which the check searches for the services
84+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
85+
*/
4486
public static ModeledAsServiceInContext beModeledAsServiceInCML(BoundedContext cmlContext) {
4587
return ModeledAsServiceInContext.beModeledAsServiceInCML(cmlContext);
4688
}
4789

90+
/**
91+
* Checks whether a class, given by its simple name, is represented as a repository in CML.
92+
*
93+
* @param boundedContext the CML Bounded Context within which the check searches for the repositories
94+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
95+
*/
4896
public static ModeledAsRepositoryInContext beModeledAsRepositoryInCML(BoundedContext boundedContext) {
4997
return ModeledAsRepositoryInContext.beModeledAsRepositoryInCML(boundedContext);
5098
}
5199

100+
/**
101+
* Checks whether an Java package representing an Aggregate, contains the same objects (Entities, Value Objects, Domain Events)
102+
* as the corresponding Aggregate in CML.
103+
*
104+
* @param boundedContext the CML Bounded Context within which the check searches for the Aggregates
105+
* @param tacticDDDAnnotationSet the set of Java annotations that is used to identify entities, value objects, and domain events within
106+
* the package. Provide an implementation of the {@link TacticDDDAnnotationSet} interface (
107+
* custom or, for example, {@link org.contextmapper.archunit.annotations.JMoleculesTacticAnnotationSet}).
108+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaPackage}
109+
*/
52110
public static AdhereToCmlAggregateStructure adhereToCmlAggregateStructure(BoundedContext boundedContext,
53111
TacticDDDAnnotationSet tacticDDDAnnotationSet) {
54112
return AdhereToCmlAggregateStructure.adhereToCmlAggregateStructure(boundedContext, tacticDDDAnnotationSet);
55113
}
56114

115+
/**
116+
* Checks whether a classes fields are modeled in a CML entity.
117+
*
118+
* @param boundedContext the CML Bounded Context within which the check searches for the corresponding entities (by the classes simple names)
119+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
120+
*/
121+
public static AdhereToCmlEntityStructure adhereToCmlEntityStructure(BoundedContext boundedContext) {
122+
return AdhereToCmlEntityStructure.adhereToCmlEntityStructure(boundedContext);
123+
}
124+
125+
/**
126+
* Checks whether a classes fields are modeled in a CML value object.
127+
*
128+
* @param boundedContext the CML Bounded Context within which the check searches for the corresponding value objects (by the classes simple names)
129+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
130+
*/
131+
public static AdhereToCmlValueObjectStructure adhereToCmlValueObjectStructure(BoundedContext boundedContext) {
132+
return AdhereToCmlValueObjectStructure.adhereToCmlValueObjectStructure(boundedContext);
133+
}
134+
135+
/**
136+
* Checks whether a classes fields are modeled in a CML domain event.
137+
*
138+
* @param boundedContext the CML Bounded Context within which the check searches for the corresponding domain events (by the classes simple names)
139+
* @return returns an {@link com.tngtech.archunit.lang.ArchCondition} for a {@link com.tngtech.archunit.core.domain.JavaClass}
140+
*/
141+
public static AdhereToCmlDomainEventStructure adhereToCmlDomainEventStructure(BoundedContext boundedContext) {
142+
return AdhereToCmlDomainEventStructure.adhereToCmlDomainEventStructure(boundedContext);
143+
}
144+
57145
}

0 commit comments

Comments
 (0)