jqassistant-archive / jqassistant-java-ddd-plugin

Annotations to explicitly mark concepts of DDD in Java including @jQAssistant constraints to enforce common concepts.
Apache License 2.0
5 stars 1 forks source link
ddd java jqassistant plugin

NOTE: This project was superseded jMolecules and its integration. All information can be found here: https://github.com/jqassistant-contrib/jqassistant-jmolecules-plugin[jMolecules-jQAssistant-Plugin]

= jQAssistant DDD Plugin

The jQAssistant DDD Plugin provides means to map DDD language elements onto the code and to enrich this information in the graph in order to apply additional, DDD-general as well as project specific, concepts and constraints.

== Usage of the jQAssistant DDD Plugin

To make use of the provided annotations (as will be shown in the next section), following dependency is needed to be defined:

[source, xml]

org.jqassistant.contrib.plugin jqassistant-java-ddd-annotations ${jqassistant.version}

Additionally, to have jQAssistant consider the annotations and to allow the execution of constraints, following dependency in the jqassistant-maven-plugin needs to be defined:

[source, xml]

com.buschmais.jqassistant jqassistant-maven-plugin ${jqassistant.version} default-cli scan analyze java-ddd:Default // <2> // <1> org.jqassistant.contrib.plugin jqassistant-java-ddd-plugin ${jqassistant.version}
<1> Definition of the plugin dependency <2> Definition of the java-ddd:Default group to be executed == DDD Concepts The jQAssistant DDD plugin comes with jQA-concepts and Java-annotations for multiple DDD concepts. The supported DDD-concepts and how they are mapped in from source code to the graph is shown in the following list. All supported DDD-concepts can be applied both on package level (by annotating the package definition in a `package-info.java` file) or class level (by annotating a Java class). === Bounded Context A bounded context is a well-defined, functional consistent, and cohesive subset of the complete solution domain. It therewith represents the implementation of one functional slice of the problem space. A bounded context is identified by its name. In case that multiple bounded contexts with the same name were declared, only one node will be created. The bounded context nodes will be labeled by `:DDD:BoundedContext`. Package Level:: All classes in the annotated package and its sub packages will be assigned to the corresponding node. .Definition of a bounded context and its contained types at package level. [source,java] ---- @BoundedContext(name = "catalog") package com.buschmais.shop.catalog; ---- Type Level:: The annotated class will be assigned to the corresponding node. .Definition of a bounded context and its contained type using at class level. [source,java] ---- @BoundedContext(name = "catalog") public class Product { } ---- Bounded Context Dependencies:: Besides defining a bounded context with a name it's also possible to define allowed dependencies between dependencies. The definition is done by specifying a list of bounded contexts by their name on which the defining bounded context may depend on. .Definition of allowed dependencies between bounded contexts. [source,java] ---- @BoundedContext(name = "catalog", dependsOn = {"merchant"}) ---- === Domain Event A domain event is a notice about the change of state in a functional sense, to which interested parties can listen. Package Level:: All classes in the annotated package will be labeled as `:DDD:DomainEvent`. .Definition of domain events at the package level. [source,java] ---- @DomainEvent package com.buschmais.shop.catalog.event; ---- Type Level:: The annotated class will be labeled as `:DDD:DomainEvent`. .Definition of domain events at the type level. [source,java] ---- @DomainEvent public class ProductCreatedEvent { } ---- === Aggregate An aggregate is a logical, cohesive group of entities and value objects from the same bounded context. Manipulations to an aggregate or its contained entities and value objects may only be done through the aggregate root which itself represents some kind of an transaction boundary. Aggregates are used to group domain objects which need to stay absolutely consistent to each other and thus are rather small. Package Level:: All classes in the annotated package will be labeled as `:DDD:AggregateRoot`. .Definition of aggregates at the package level. [source,java] ---- @AggregateRoot package com.buschmais.shop.catalog.model.aggregateroot; ---- Type Level:: The annotated class will be labeled as `:DDD:Aggregate`. .Definition of aggregates at the type level. [source,java] ---- @AggregateRoot public class Product { } ---- === Entity An entity is a domain object which is not defined by its properties but by its unique identifier which will not change throughout its existence. Note: Both the `@Entity`-annotation provided by this plug-in as well as `@javax.persistence.Entity` will be treated as DDD entities. Package Level:: All classes in the annotated package will be labeled as `:DDD:Entity`. .Definition of entities at the package level. [source,java] ---- @Entity package com.buschmais.shop.catalog.model.entity; ---- Type Level:: The annotated class will be labeled as `:DDD:Entity`. .Definition of entities at the type level. [source,java] ---- @Entity public class Product { } ---- === Value Object Value objects are immutable domain objects which have no unique identifies but are identified by their properties. Package Level:: All classes in the annotated package will be labeled as `:DDD:ValueObject`. .Definition of value objects at the package level. [source,java] ---- @ValueObject package com.buschmais.shop.catalog.model.valueobject; ---- Type Level:: The annotated class will be labeled as `:DDD:ValueObject`. .Definition of value objects at the type level. [source,java] ---- @ValueObject public class Price { } ---- === Service A service is a stateless object providing access to domain objects and implementing business rules as methods (commands and queries). Services operate on aggregates. Package Level:: All classes in the annotated package will be labeled as `:DDD:Service`. .Definition of services at the package level. [source,java] ---- @Service package com.buschmais.shop.catalog.service; ---- Type Level:: The annotated class will be labeled as `:DDD:Service`. .Definition of services at the type level. [source,java] ---- @Service public class ProductService { } ---- === Repository A repository represents an accessor to a persistent store by both providing functionality to create and modify domain objects. Repositories operate on an aggregate. Package Level:: All classes in the annotated package will be labeled as `:DDD:Repository`. .Definition of repositories at the package level. [source,java] ---- @Repository package com.buschmais.shop.catalog.repository; ---- Type Level:: The annotated class will be labeled as `:DDD:Repository`. .Definition of repositories at the type level. [source,java] ---- @Repository public class ProductRepository { } ---- === Factory A factory takes care of creating a new entity or value object from given data or an already existing object and takes care of its invariants. A factory (method) can be present directly in the domain model class or as a separate class. Package Level:: All classes in the annotated package will be labeled as `:DDD:Factory`. .Definition of factories at the package level. [source,java] ---- @Factory package com.buschmais.shop.catalog.factory; ---- Type Level:: The annotated class will be labeled as `:DDD:Factory`. .Definition of factories at the type level. [source,java] ---- @Factory public class ProdutFactory { } ---- === Layer Besides the definition of functional concepts in DDD there are also requirements to the technical layering of the application stated. Per layer, a new node labeled as ':DDD:Layer' with a name property will be created. All classes annotated as being part of a specific layer will be associated to the respective layer node using a 'CONTAINS' relation. Following nodes will be created: * (:DDD:Layer {name: 'Interface'}) * (:DDD:Layer {name: 'Application'}) * (:DDD:Layer {name: 'Domain'}) * (:DDD:Layer {name: 'Infrastructure}) ==== InterfaceLayer The interface layer is the outermost layer in a DDD-architecture, providing access to the application to other services and the user. This layer is very thin and provides only rudimentary functionality for e.g. request handling. No domain logic shall be implemented by this layer. Package Level:: All classes in the annotated package will associated to the (:DDD:Layer {name: 'Interface'}) node. .Assignement to the interface layer at the package level. [source,java] ---- @InterfaceLayer package com.buschmais.shop.catalog.interfaces; ---- Type Level:: The annotated class will be associated to the (:DDD:Layer {name: 'Interface'}) node. .Assignment to the interface layer at the type level. [source,java] ---- @InterfaceLayer public class ProductController { } ---- ==== ApplicationLayer The application layer is a thin layer orchestrating business use cases and spanning transactions. It implements no specific domain logic but coordinates the correct execution of scenarios and takes care of aspects like transaction handling. Package Level:: All classes in the annotated package will be associated to the (:DDD:Layer {name: 'Application'}) node. .Assignement to the application layer at the package level. [source,java] ---- @ApplicationLayer package com.buschmais.shop.catalog.application; ---- Type Level:: The annotated class will be associated to the (:DDD:Layer {name: 'Application'}) node. .Assignment to the application layer at the type level. [source,java] ---- @ApplicationLayer public class ProductHandler { } ---- ==== DomainLayer The domain layer is the heart of a DDD-structured application and implements the business logic and objects of bounded contexts. Package Level:: All classes in the annotated package will be associated to the (:DDD:Layer {name: 'Domain'}) node. .Assignement to the domain layer at the package level. [source,java] ---- @DomainLayer package com.buschmais.shop.catalog.domain; ---- Type Level:: The annotated class will be associated to the (:DDD:Layer {name: 'Domain'}) node. .Assignment to the domain layer at the type level. [source,java] ---- @DomainLayer public class ProductService { } ---- ==== InfrastructureLayer The infrastructure layer is the supporting layer for the other layers providing technical implementations like database access. Infrastructure can both be present in the bounded context scope (like when providing access to the product table) or in global scope, e.g. for sending e-mails. Package Level:: All classes in the annotated package will be associated to the (:DDD:Layer {name: 'Infrastructure}) node. .Assignement to the infrastructure layer at the package level. [source,java] ---- @InfrastructureLayer package com.buschmais.shop.catalog.infrastructure; ---- Type Level:: The annotated class will be associated to the (:DDD:Layer {name: 'Infrastructure}) node. .Assignment to the infrastructure layer at the type level. [source,java] ---- @InfrastructureLayer public class ProductRepositoryImpl { } ---- == Default DDD Constraints The jQAssistant DDD plug-in comes with several pre-defined constraints which check the implemented architecture against the basic DDD architectural principles. The following constraints will be active by default and configured with a `Major`-severity. === java-ddd:TypeInMultipleLayers The constraint checks that each type is only part of one layer. === java-ddd:TypeInMultipleBoundedContexts The constraint checks that each type is only part of one bounded context. === java-ddd:IllegalDependenciesBetweenBoundedContexts The constraints checks that there are no dependencies between bounded contexts present when they are not defined. === java-ddd:UnneededDependenciesBetweenBoundedContexts The constraint checks that there are no dependencies between bounded contexts defined which are not required by the implementation. == Strict DDD Constraints Additionally to the default DDD constraints which are quite relaxed and thus easy to integrate into legacy projects, there is a more strict set of constraints provided by the DDD plug-in. The constraints can be activated by declaring the name of it in the executed groups section of the plug-in configuration: [source, xml] ---- java-ddd:Default // <1> java-ddd:Strict // <2> ---- <1> Activation of the default rule set. <2> Activation of the strict rule set. === java-ddd:IllegalDependenciesBetweenTechnicalLayers As defined by DDD, only dependencies between specific layers are allowed. These dependencies are visualized in the following diagram: [plantuml,layer-dependencies,png] ---- skinparam componentStyle uml2 component { component InterfaceLayer component ApplicationLayer component DomainLayer } component InfrastructureLayer InterfaceLayer --> ApplicationLayer InterfaceLayer --> DomainLayer ApplicationLayer --> DomainLayer InfrastructureLayer --> InterfaceLayer InfrastructureLayer -left-> ApplicationLayer InfrastructureLayer --> DomainLayer ---- The constraint checks for the correct implementation of the relations. == Visual Reporting The DDD plug-in supports visualization of concepts using PlantUML in jQAssistant reports for the following concepts: * Bounded Context * Layer (Interface, Application, Domain, Infrastructure) To use this functionality, define the jQAssistant concept with the following property set: * `reportType="plantuml-component-diagram"` == Licensing The `jqassistant-java-ddd-annotations` module is licensed under the Apache License, Version 2.0. Therefore, it can be added as a direct dependency to the project without the need to disclose the source code. The `jqassistant-java-ddd-plugin` module is licensed under the GNU GENERAL PUBLIC LICENSE, Version 3 due to the dependency to Neo4j. However, as this will be declared as a dependency of the `jqassistant-maven-plugin` (which is a build plugin), it will not end up in the delivery.