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:

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:

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 and analyze 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.

pom.xml
<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.

src/main/java/your/company/project/order/package-info.java
@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.

src/main/java/your/company/project/order/OrderService.java
@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

  1. 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.

  2. 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:

jqassistant/architecture.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 constraint definition as defined in jqassistant/architecture.adoc
=== 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

Bounded Context overview as defined in jqassistant/architecture.adoc
=== 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:

architecture BoundedContextOverview

8. Resources