Demonstrates how to map DDD concepts in the code to the graph and apply constraints using the DDD plugin,
Note
|
This tutorial has been written for jQAssistant 1.7.0 |
1. Prerequisites
-
Any Java application which implements (or shall implement) DDD concepts
2. Overview
The DDD for jQAssistant plugin provides the ability to map well-known DDD concepts to the source code and finally to the generated graph. Based on this, it allows to define and verify DDD-specific constraints.
Detected violations will be printed as warnings at the end of the build or might even break it if required.
The steps in this tutorial illustrate:
-
Setup of the jQAssistant DDD Plugin
-
Checking the Pre-Defined Rules
-
Using the Pre-Defined Concepts
-
Using the Pre-Defined Constraints
-
Definition of Project Specific Rules
3. jQAssistant DDD Plugin
jQAssistant runs as part of the build process and therefore needs to be integrated as Maven plugin.
This is done by adding the following setup to the build/plugins
section of the file pom.xml
:
<plugin>
<groupId>com.buschmais.jqassistant</groupId>
<artifactId>jqassistant-maven-plugin</artifactId>
<version>${jqassistant.version}</version>
<executions>
<execution>
<id>default-cli</id>
<goals>
<goal>scan</goal>
<goal>analyze</goal>
</goals>
<configuration>
<groups>
<group>java-ddd:Default</group>
<group>architecture:Default</group>
</groups>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jqassistant.contrib.plugin</groupId>
<artifactId>jqassistant-java-ddd-plugin</artifactId>
<version>${jqassistant.version}</version>
</dependency>
<dependency>
<groupId>org.jqassistant.contrib.plugin</groupId>
<artifactId>jqassistant-asciidoc-report-plugin</artifactId>
<version>${jqassistant.version}</version>
</dependency>
</dependencies>
</plugin>
The configuration above…
-
activates the goals
scan
andanalyze
during a build -
adds the jQAssistant DDD plugin
-
adds the jQAssistant AsciiDoc Report plugin which is needed during this tutorial
Furthermore, to make use of the provided annotations, the DDD plugin must be added as maven dependency to the project as shown below.
<dependency>
<groupId>org.jqassistant.contrib.plugin</groupId>
<artifactId>jqassistant-java-ddd-plugin</artifactId>
<version>${jqassistant.version}</version>
<scope>provided</scope>
</dependency>
The Maven build can be triggered as usual on the command line:
mvn clean install
4. Pre-Defined Rules
The setup above activates the pre-defined group java-ddd:Default
that provides some basic constraints for
the structure and architecture of the project.
Executing the goal effective-rules
on the command line using
mvn jqassistant:effective-rules
prints a summary of the activated rules including their descriptions:
[INFO] Groups [1] [INFO] "java-ddd:Default" [INFO] Constraints [4] [INFO] "java-ddd:IllegalDependenciesBetweenBoundedContexts" - Checks that dependencies between bounded contexts are present only where defined. [INFO] "java-ddd:TypeInMultipleBoundedContexts" - Checks that a single DDD type is only part of one bounded context. [INFO] "java-ddd:TypeInMultipleLayers" - Checks that a single DDD type is only part of one layer. [INFO] "java-ddd:UnneededDependenciesBetweenBoundedContexts" - Checks that dependencies between bounded contexts are defined only where needed. [INFO] Concepts [21] [INFO] "java-ddd:AggregateRootPackage" - Labels all Java types which are located in package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.AggregateRoot as :DDD:AggregateRoot. [INFO] "java-ddd:AggregateRootType" - Labels all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.AggregateRoot as :DDD:AggregateRoot. [INFO] "java-ddd:BoundedContextDependency" - Propagates the dependencies between Types of different Bounded Contexts to the level of Bounded Contexts including an aggregated weight. [INFO] "java-ddd:BoundedContextPackage" - Maps all Java types which are located in a package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.BoundedContext to the corresponding BoundedContext node. [INFO] "java-ddd:BoundedContextType" - Maps all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.BoundedContext to the corresponding BoundedContext node. [INFO] "java-ddd:DefinedBoundedContextDependencies" - Create the defined allowed dependencies between bounded contexts. [INFO] "java-ddd:DomainEventPackage" - Labels all Java types which are located in a package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.DomainEvent as :DDD:DomainEvent. [INFO] "java-ddd:DomainEventType" - Labels all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.DomainEvent as :DDD:DomainEvent. [INFO] "java-ddd:EntityPackage" - Labels all Java types which are located in package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Entity as :DDD:Entity. [INFO] "java-ddd:EntityType" - Labels all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Entity or javax.persistence.Entity as :DDD:Entity. [INFO] "java-ddd:FactoryPackage" - Labels all Java types which are located in a package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Factory as :DDD:Factory. [INFO] "java-ddd:FactoryType" - Labels all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Factory as :DDD:Factory. [INFO] "java-ddd:LayerPackage" - Associates all Java types in packages which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Layer.X to the respective layer. [INFO] "java-ddd:LayerType" - Associates all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Layer.X to the respective layer. [INFO] "java-ddd:PrepareBoundedContext" - Creates a bounded context node per defined bounded context (identified by name) based on org.jqassistant.contrib.plugin.ddd.annotation.DDD$BoundedContext. [INFO] "java-ddd:PrepareLayer" - Creates a node for each of the following layer: Interface, Application, Domain, Infrastructure. [INFO] "java-ddd:RepositoryPackage" - Labels all Java types which are located in a package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Repository as :DDD:Repository. [INFO] "java-ddd:RepositoryType" - Labels all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Repository as :DDD:Repository. [INFO] "java-ddd:ServicePackage" - Labels all Java types which are located in a package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Service as :DDD:Service. [INFO] "java-ddd:ServiceType" - Labels all Java types which are annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.Service as :DDD:Service. [INFO] "java-ddd:ValueObjectPackage" - Labels all Java types which are located in a package annotated by org.jqassistant.contrib.plugin.ddd.annotation.DDD.ValueObject as :DDD:ValueObject. [INFO] "java-ddd:ValueObjectType" - Labels all Java types which are annotated by javax.persistence.Embeddable or org.jqassistant.contrib.plugin.ddd.annotation.DDD.ValueObject as :DDD:ValueObject.
5. Pre-Defined Concepts
The DDD plugin comes with pre-defined jQAssistant concepts. For each DDD concept, there is a mapping on type and package level.
With that, you can either declare all classes in a package (and its sub-packages) as, e.g. part of a bounded context as shown below.
@DDD.BoundedContext(name = "order", dependsOn = {"catalog"})
package your.company.project.order;
import org.jqassistant.contrib.plugin.ddd.annotation.DDD;
Or directly by annotating a specific Java class as shown next.
@DDD.Service
public class OrderService {
6. Pre-Defined Constraints
The DDD plugin comes with a set of pre-defined constraints checking the most basic architecture violations possible. Following things will be checked during build time:
-
java-ddd:IllegalDependenciesBetweenBoundedContexts
— If there are dependencies between layers which were not defined -
java-ddd:UnneededDependenciesBetweenBoundedContexts
— If there are dependencies between bounded contexts defined which are actually not used -
java-ddd:TypeInMultipleBoundedContexts
— If there are types assigned to multiple bounded contexts -
java-ddd:IllegalDependenciesBetweenBoundedContexts
— If there are dependencies between bounded contexts which were not defined
7. Project Specific Rules
The concepts defined by the DDD plugin are also a good base for project specific rules. This is especially true as the plugin comes only with a few, relaxed constraints.
There are two use cases imaginable for the DDD plugin
-
A given application shall be refactored to match a DDD-like structure. This refactoring has to take place during daily development and in small steps as continuous improvement steps. The DDD plugin will be used to track and secure the improvements.
-
A new application shall be implemented with a DDD-like structure. The DDD plugin will be used from start on to verify that the designed architecture is actually implemented.
Depending on the state of the application, it is possible to define additional, more or less strict constraints. e.g that only aggregate roots may be accessible through repositories.
The rules must be located in /jqassistant
and can be written either in XML or Asciidoc files, where the latter approach is recommended.
The following examples will be defined in the file jqassistant/architecutre.adoc
:
[[architecture:Default]] [role=group,includesConstraints="architecture:*",includesConcepts="architecture:*"] == Architecture Documentation
It defines a group architecture:Default
which must be activated in the pom.xml
file (see pom in section jQAssistant DDD Plugin above).
This group contains all constraints and concepts matching the definition.
7.1. Adding DDD Constraints
Besides the group definition, the example defines that repositories are allowed to only return aggregates which will be checked by
-
the constraint
architecture:AggregateRepository
that ensures that only DDD aggregates (i.e. classes annotated with@DDD.Aggregate
) are returned by repositories (i.e. classes annotated with@DDD.Repository
)
=== Aggregate Rules [[architecture:AggregateRepository]] [source,cypher,role=constraint,requiresConcepts="java-ddd:Aggregate*,java-ddd:Repository*"] .Only aggregates are allowed to be returned by repositories. ---- MATCH (repo:DDD:Repository)-[:DECLARES]->(:Method)-[:RETURNS]->(t:Type) WHERE NOT t:DDD:AggregateRoot RETURN repo.fqn as Repository, t.fqn as IllegalReturnType ----
The project can be built and verified by running the following command:
mvn clean install
7.2. Visualization of Building Blocks
As shown earlier, the plugin comes with annotations for identifying technical layers and bounded contexts in the source code.
The current structure of the application can, when using those annotations (i.e. @DDD.BoundedContext
and @DDD.Layer.<classifier>Layer
)
be easily visualized by automatically generated plantuml diagrams.
The generation of the component diagrams will be accomplished by the added jQAssistant AsciiDoc Report plugin.
An example to visualize the defined bounded contexts including their defined and actual dependencies is shown in the following listing. It defines:
-
the concept
architecture:BoundedContextOverview
that identifies all defined bounded contexts and their dependencies
=== Whitebox View [[architecture:BoundedContextOverview]] [source,cypher,role=concept,requiresConcepts="java-ddd:*",reportType="plantuml-component-diagram"] .Documentation of existing bounded contexts and their dependencies ---- MATCH (bc1:DDD:BoundedContext) OPTIONAL MATCH (bc1)-[d:DEFINES_DEPENDENCY|DEPENDS_ON]->(bc2) WHERE bc1 <> bc2 RETURN bc1, d, bc2 ----
Finally the group architecture:Default
must be activated in the pom.xml
file (see pom in section jQAssistant DDD Plugin above).
The project can be built and verified by running the following command:
mvn clean install
The resulting component diagram will look as follows: