diff --git a/architecture-decision-records/.jqassistant.yml b/architecture-decision-records/.jqassistant.yml new file mode 100644 index 0000000..420654b --- /dev/null +++ b/architecture-decision-records/.jqassistant.yml @@ -0,0 +1,13 @@ +jqassistant: + plugins: + #<1> + - group-id: org.jqassistant.contrib.plugin + artifact-id: jqassistant-jmolecules-plugin + version: 1.4.0 + + analyze: + groups: + #<2> + - jmolecules-ddd:Default + #<3> + - Default \ No newline at end of file diff --git a/architecture-decision-records/index.adoc b/architecture-decision-records/index.adoc deleted file mode 100644 index ff32347..0000000 --- a/architecture-decision-records/index.adoc +++ /dev/null @@ -1,247 +0,0 @@ -:imagesdir: includes - -= Documenting and Securing architecture decisions with ADRs and jQAssistant -Stephan Pirnbaum - -:numbered: - -[.lead] -// tag::lead[] -Demonstrates how Architecture Decision Records can be enhanced with jQAssistant. -// end::lead[] - -NOTE: This tutorial has been written for jQAssistant 1.8.0 - -== Prerequisites - -- Any Java application for which an ADR shall be written. - -== Overview - -jQAssistant comes with the ability to render AsciiDoc documents and to execute rules (i.e. concepts and constraints) which were defined inside them. - -With that it qualifies perfectly to connect with Michael Nygards Architecture Decision Records to document architectural decisions and to enforce their adherence. - -This tutorial will show: - -- <> -- <> -- <> - -For this, the tutorial uses the following example scenario: - -The database for the product catalog of an online shop shall be migrated from a relational to a documented-oriented one. -This decision was made due to performance and scalability reasons, but is out of scope for this example. - -However, document-oriented databases requires the modeling of aggregates in the data model and, above that, forbid deep links between aggregates. -E.g., an entity must be referenced only inside one aggregate. - -Thus, the decision was made to prepare the migration by implementing aggregates and removing deep links between them. -The example is therefore build around the deep linking decision. - -[[what]] -== What are Architecture Decision Records? - -Architecture Decision Records are a lightweight way to document architectural decisions. They were first described by Michael Nygard (see [2]) and aim to: - -- spread the knowledge about architectural decisions to the team -- make the decisions and their history transparent -- provide context and implications in a nutshell - -Architecture Decision Records consist of the following parts: - -.Sections of ADRs -|=== -|Section |Content - -|Title -|Numbered title of the ADR - -|Status -|Current phase in the lifecycle of ADRs, one of: Proposed, Accepted, Declined, Superseded. - -|Context -|Brief description of the context for which the decision is valid. Describes the forces which lead to the decision. - -|Decision -|Overview of the agreed on decision. - -|Consequences -|Lists the implications for the developers both for exisitng and new source code. This may also contain needed refactorings. -|=== - - -[[setup]] -== Setting up Architecture Decision Records in the Project - -First, jQAssistant needs to be setup in the project. -Therefore, the `jqassistant-maven-plugin` must be added to the build plugin section in the `pom.xml` s shown in the following listing. - -[[pom]] -[source,xml] -.pom.xml ----- -include::pom.xml[tag=plugin,indent=0] ----- - -NOTE: The additional `jqassistant-java-ddd-plugin` is not needed to work with ADRs, but will be used later to simplify the example. -Information about the DDD-Plugin can be found at [3]. - -Secondly, the folder and file structure must be created as shown in <>. -The `index.adoc` is the entry point for jQAssistant and will include the other documents. - -NOTE: For larger projects, it's helpful to group ADRs by topic into subfolders, -e.g. one folder for Structure-related topics, one for Logging, one for Testing, and so on. - -[[adrFlat]] -[plantuml, format=svg] -.Folder Structure for ADRs ----- -skinparam Legend { - BackgroundColor transparent - BorderColor transparent - FontSize 17 -} -legend - -|_ jqassistant - |_ index.adoc - |_ 001-First-Example-ADR.adoc - |_ 002-Second-Element-ADR.adoc - |_ ... -end legend ----- - -The following listing shows the basic structure of the `index.adoc`. -It's used to build an index with cross-references to other documents and to include them. - -[source,asciidoc] -.jqassistant/index.adoc ----- - = Example project for Architecture Decision Records - - - <> // <1> - - include::001-Deep-Linking.adoc[] // <2> ----- -<1> Generates a link to the anchor defined in the ADR -<2> Includes the ADR - -The following listing shows the basic structure of an Architecture Decision Record. -The complete ADR can be found at `jqassistant/001-Deep-Linking.adoc`. - -Important to mention is the definiton of the anchor `adr:DeepLinking` which can be used for creating cross-references in the document. - -[source,asciidoc] -.jqassistant/001-Deep-Linking.adoc ----- - [[adr:DeepLinking]] // <1> - == 001 - No deep linking between aggregates - - === Status: Proposed // <2> - - === Context // <3> - ... - - === Decision // <4> - ... - - === Consequences // <5> - ... ----- -<1> Anchor for referencing the ADR -<2> Status of the ADR -<3> Context section of the ADR -<4> Decision section of the ADR -<5> Consequences section of the ADR - -NOTE: For the full example, please check out the ADR directly. - -[[securing]] -== Defining Concepts and Constraints to Secure Decisions - -Now that the basic setup is done, it is time to check that the architecture decision will be implemented correctly. - -For that, two parts are crucial: - -1. Identify aggregates in the code -2. Determine violations regarding deep-linking - -For this, jQAssistant knows concepts (1) and constraints (2). - -With concepts it's possible to enrich the created graph with additional information, e.g. that a type node represents an Aggregate (Root). - -To simplify the 101, we'll use the `jqassistant-java-ddd-plugin` to take care of this. -Further information how to use it can be found in the 101 about the plugin (see [3]). - -In our example, after integrating the plugin as shown in the <>, we have to annotate all aggregate roots as `@DDD.AggregateRoot` and the entities as `@DDD.Entity`. -That way, the information will be automatically enriched in the graph. - -NOTE: It's also possible to enrich the information manually via a self-defined concept. In this 101, it's however only shown how to define the constraint. - -Next, the we need to define the constraint in the ADR. -The following listing shows how to do this. - -[source,asciidoc] -.Defining a Constraint in jqassistant/001-Deep-Linking.adoc ----- - [[adr:DeepLinking]] - [role=group,includesConstraints="adr:*"] // <1> - == 001 - No deep linking between aggregates - ... - === Consequences - ... - - [[adr:NoDeepLinkingBetweenAggregates]] // <2> - [source,cypher,role=constraint,requiresConcepts="java-ddd:*"] // <3> - .Finds all entities that are referenced by more than one aggregate. - ---- - MATCH shortestPath((a:DDD:AggregateRoot)-[:DEPENDS_ON*]->(e:DDD:Entity)) // <4> - WITH e, collect(a.name) AS aggregates - WHERE size(aggregates) > 1 - RETURN e.name AS Entity, aggregates AS Aggregates - ---- ----- -<1> Defines the jQAssistant group and what constraints will be executed -<2> Defines the name of the constraint -<3> Defines the jQAssistant constraint and required concepts, here the DDD-Plugin -<4> Defines the query to execute - -Afterward, the group must be added to the default group of jQAssistant to be executed as shown below. - -[source,asciidoc] -.Referencing the Group in jqassistant/index.adoc ----- - ... - [[default]] // <1> - [role=group,includesGroups="adr:*"] // <2> - - <> - ... ----- -<1> Defines the name of the group -<2> Defines what groups are contained - -When the project now gets build via maven, you'll see a constraint violation which leads to a failing build. - ----- -[ERROR] --[ Constraint Violation ]----------------------------------------- -[ERROR] Constraint: adr:NoDeepLinkingBetweenAggregates -[ERROR] Severity: MAJOR -[ERROR] Number of rows: 1 -[ERROR] Finds all entities that are referenced by more than one aggregate. -[ERROR] Entity=ProductOption, Aggregates=Category, Product -[ERROR] ------------------------------------------------------------------- ----- - -Whenever there will be a new violation, the build will fail and we are forced to fix the issue. -However, when introducing a new decision, it may make sense to either lower the severity of the constraint to `MINOR` -in order to not fail build or to update the query to have a list of allowed violations. -That way, the decision can be incorporated step-by-step. - -== Resources - -Architecture Decision Records are a lightweight way to document architectural decision. They were first described by Michael Nygard in [ - -1. link:tutorial.zip[ZIP archive including the application] -2. link:http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions[Architecture Decision Records by Michael Nygard] -3. link:https://101.jqassistant.org/ddd-plugin/index.html[jQAssistat 101 on Domain Driven Design] \ No newline at end of file diff --git a/architecture-decision-records/jqassistant/001-Deep-Linking.adoc b/architecture-decision-records/jqassistant/001-Deep-Linking.adoc deleted file mode 100644 index 4c8e925..0000000 --- a/architecture-decision-records/jqassistant/001-Deep-Linking.adoc +++ /dev/null @@ -1,29 +0,0 @@ -[[adr:DeepLinking]] -[role=group,includesConstraints="adr:*"] -== 001 - No deep linking between aggregates - -=== Status: Proposed - -=== Context - -* Migration of the product catalog from relational to document-based database - -=== Decision - -* Document-based databases require modeling of aggregates -** Deep links between aggregates are not allowed - -=== Consequences - -* All entities must only be referenced from one aggregate root -* Existing deep linking between aggregates must be removed - -[[adr:NoDeepLinkingBetweenAggregates]] -[source,cypher,role=constraint,requiresConcepts="java-ddd:*"] -.Finds all entities that are referenced by more than one aggregate. ----- -MATCH shortestPath((a:DDD:AggregateRoot)-[:DEPENDS_ON*]->(e:DDD:Entity)) -WITH e, collect(a.name) AS aggregates -WHERE size(aggregates) > 1 -RETURN e.name AS Entity, aggregates AS Aggregates ----- \ No newline at end of file diff --git a/architecture-decision-records/jqassistant/001-Deep-Linking.xml b/architecture-decision-records/jqassistant/001-Deep-Linking.xml new file mode 100644 index 0000000..8266207 --- /dev/null +++ b/architecture-decision-records/jqassistant/001-Deep-Linking.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + Finds all entities that are referenced by more than one aggregate. + + + (e:JMolecules:DDD:Entity) + WITH + e, collect(a.name) AS aggregates + WHERE + size(aggregates) > 1 + RETURN + e.name AS Entity, aggregates AS Aggregates + ]]> + + + + + \ No newline at end of file diff --git a/architecture-decision-records/jqassistant/default.xml b/architecture-decision-records/jqassistant/default.xml new file mode 100644 index 0000000..9014b18 --- /dev/null +++ b/architecture-decision-records/jqassistant/default.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/architecture-decision-records/jqassistant/index.adoc b/architecture-decision-records/jqassistant/index.adoc deleted file mode 100644 index db3411a..0000000 --- a/architecture-decision-records/jqassistant/index.adoc +++ /dev/null @@ -1,7 +0,0 @@ -= Example project for Architecture Decision Records - -[[default]] -[role=group,includesGroups="adr:*"] -- <> - -include::001-Deep-Linking.adoc[] \ No newline at end of file diff --git a/architecture-decision-records/pom.xml b/architecture-decision-records/pom.xml index d590d8f..223aeb7 100644 --- a/architecture-decision-records/pom.xml +++ b/architecture-decision-records/pom.xml @@ -9,24 +9,23 @@ 0.0.1-SNAPSHOT jar - - 1.8.0 - - + - org.jqassistant.contrib.plugin - jqassistant-java-ddd-annotations - ${jqassistant.version} + org.jmolecules + jmolecules-ddd + 1.6.0 + + - + com.buschmais.jqassistant jqassistant-maven-plugin - ${jqassistant.version} + 2.0.4 @@ -35,15 +34,40 @@ + + + + + org.asciidoctor + asciidoctor-maven-plugin + 2.2.4 + + html5 + true + + ${project.build.directory}/jqassistant/jqassistant-report.xml + + + + + + output-html + verify + + process-asciidoc + + + + - org.jqassistant.contrib.plugin - jqassistant-java-ddd-plugin - ${jqassistant.version} + org.jqassistant.tooling.asciidoctorj + jqassistant-asciidoctorj-extensions + 1.0.0 - + diff --git a/architecture-decision-records/readme.adoc b/architecture-decision-records/readme.adoc new file mode 100644 index 0000000..dbd0fbc --- /dev/null +++ b/architecture-decision-records/readme.adoc @@ -0,0 +1,329 @@ +:imagesdir: includes + += Documenting and Securing architecture decisions with ADRs and jQAssistant +Stephan Pirnbaum + +:numbered: + +[.lead] +// tag::lead[] +This tutorial demonstrates how Architecture Decision Records (ADRs) can be enhanced with jQAssistant. The tutorial shows how jQAssistant can display dynamic results directly inside your ADRs. +// end::lead[] + +NOTE: This tutorial has been written for jQAssistant 2.0.4 + +== Prerequisites + +- Any Java application for which an ADR shall be written. + +== Overview + +jQAssistant comes with the ability to execute rules (i.e. groups, concepts and constraints) which can then be rendered as results inside AsciiDoc documents. + +With that it qualifies perfectly to connect with Michael Nygards Architecture Decision Records to document architectural decisions and to enforce their adherence. + +This tutorial will show: + +- <> +- <> +- <> +- <> + +For this, the tutorial uses the following example scenario: + +The database for the product catalog of an online shop shall be migrated from a relational to a document-oriented one. +This decision was made due to performance and scalability reasons, but is out of scope for this example. + +However, document-oriented databases requires the modeling of aggregates in the data model and, above that, forbid deep links between aggregates. +E.g., an entity must be referenced only inside one aggregate. + +Thus, the decision was made to prepare the migration by implementing aggregates and removing deep links between them. +The example is therefore build around the deep linking decision. + +[[what]] +== What are Architecture Decision Records? + +Architecture Decision Records are a lightweight way to document architectural decisions. They were first described by Michael Nygard (see link:http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions[ADRs by Michael Nygard]) and aim to: + +- spread the knowledge about architectural decisions to the team +- make the decisions and their history transparent +- provide context and implications in a nutshell + +Architecture Decision Records consist of the following parts: + +.Sections of ADRs +|=== +|Section |Content + +|Title +|Numbered title of the ADR + +|Status +|Current phase in the lifecycle of ADRs, one of: Proposed, Accepted, Declined, Superseded. + +|Context +|Brief description of the context for which the decision is valid. Describes the forces which lead to the decision. + +|Decision +|Overview of the agreed on decision. + +|Consequences +|Lists the implications for the developers both for exisitng and new source code. This may also contain needed refactorings. +|=== + + +[[setup]] +== Setting up your project + +=== Setting up jQAssistant + +First, jQAssistant needs to be setup in the project. +Therefore, the `jqassistant-maven-plugin` must be added to the `build/plugin` section in the `link:pom.xml[]` as shown: + +[[pom]] +[source,xml] +.link:pom.xml[] enabling jQAssistant +---- +include::pom.xml[tag=jQAplugin,indent=0] +---- + +Secondly, the project uses the `jmolecules-ddd` dependency. This dependency provides helpful annotations which are used for some predefined rules of jQAssistant. The following snippet is located in your `link:pom.xml[]` in the `dependencies` section: + +[[pom-jmolecules]] +[source,xml] +.link:pom.xml[] enabling ddd annotations +---- +include::pom.xml[tag=jMolecules,indent=0] +---- + +To use the aforementioned predefined rules, the following configurations have to be added to the `link:.jqassistant.yml[]`: + +[[yaml]] +[source,yaml] +.link:jqassistant.yml[] +---- +include::.jqassistant.yml[] +---- +<1> includes a plugin for jQAssistant which allows jQAssistant to resolve `jmolecules-ddd`-annotations. +<2> enables the execution of the `jmolecules-ddd:Default` group. This group is part of the plugin from (1) +<3> enables execution of the `Default` group (the group is defined in the <> section) + +NOTE: To see all available configuration options from the .jqassistant.yml refer to the link:https://jqassistant.github.io/jqassistant/doc/2.0.0/manual/#_yaml_files[jQAssistant user manual] + +=== Set up the adr-rules + +This tutorial uses a basic structure for the jQAssistant rules: + +[[adrFlat]] +[plantuml, format=svg] +.Folder Structure jQAssistant rules +---- +skinparam Legend { + BackgroundColor transparent + BorderColor transparent + FontSize 17 +} +legend + +|_ jqassistant + |_ index.xml + |_ 001-Deep-Linking.xml +end legend +---- + +NOTE: For larger projects, it's helpful to group ADRs by topic into sub-folders, +e.g. one folder for Structure-related topics, one for Logging, one for Testing, and so on. + +[[securing]] +== Defining Concepts and Constraints to Secure Decisions + +Now that the basic setup is done, it is time to check that the architecture decision will be implemented correctly. + +For that, two parts are crucial: + +1. Identify aggregates in the code +2. Determine violations regarding deep-linking + +For this, jQAssistant knows concepts (1) and constraints (2). + +With concepts it's possible to enrich the created graph with additional information, e.g. that a type node represents an Aggregate (Root). + +To simplify the 101, we'll use the `jqassistant-jmolecules-plugin` to take care of this. +Further information how to use it can be found in the 101 about the plugin. +//TODO: Link + +In our example, after integrating the `jmolecules-ddd` dependency (as shown in <> of this tutorial) and the `jqassistant-jmolecules-plugin` plugin (as shown in <>), we have to annotate all aggregate roots as `@AggregateRoot` and the entities as `@Entity`. +That way, the information will be automatically enriched in the jQAssistant-graph each time the aforementioned `jmolecules-ddd:Default` group is executed. + +WARNING: Be aware, that both annotations need to be imported to your .java files. Both are part of `org.jmolecules.ddd.annotation.*`. + +NOTE: The mentioned annotations are used exemplary in `link:src/main/java/your/company/adr/project/Category.java[Category.java]` and `link:src/main/java/your/company/adr/project/ProductOption.java[ProductOption.java]`. + +NOTE: It's also possible to enrich the information manually via a self-defined concept. In this 101, it's however only shown how to define the constraint on top of the pre-defined concepts the `jqassistant-jmolecules-plugin` provides. + +Next, the constraint for the ADR is created. The following snippet stems from the `link:jqassistant/001-Deep-Linking.xml[001-Deep-Linking.xml]` file. + +[source,xml] +---- +include::jqassistant/001-Deep-Linking.xml[tag=content] +---- +<1> Defines the jQAssistant group, that includes all constraints starting with `adr:DeepLinking:` +<2> Defines the name of a new constraint (the constraint starts with `adr:DeepLinking:` so it's included in the aforementioned group) +<3> Defines that the constraint requires the concepts starting with `jmolecules-ddd:`. This includes all concepts generated by the `jqassistant-jmolecules-plugin`. +<4> Sets the description for the constraint +<5> Sets the jQAssistant-query, which is executed by this constraint + +Furthermore the `link:jqassistant/default.xml[default.xml]` defines the group `Default`, that includes all rules starting with `adr:`: + +[[Default-group-use]] +[source,xml] +---- +include::jqassistant/default.xml[tag=content] +---- + +NOTE: The `Default` group is defined to be analyzed when jQAssistant is executed. This behaviour is explained in <> of this tutorial. + +Now when the project gets build via maven, you'll see a constraint violation which leads to a failing build. + +---- +[ERROR] --[ Constraint Violation ]----------------------------------------- +[ERROR] Constraint: adr:DeepLinking:NoDeepLinkingBetweenAggregates +[ERROR] Severity: MAJOR +[ERROR] Number of rows: 1 +[ERROR] Finds all entities that are referenced by more than one aggregate. +[ERROR] +[ERROR] Entity=ProductOption, Aggregates=Category, Product, Product +[ERROR] ------------------------------------------------------------------- +---- + +Whenever there will be a new violation, the build will fail and we are forced to fix the issue. +However, when introducing a new decision, it may make sense to either lower the severity of the constraint to `MINOR` +in order to not fail build or to update the query to have a list of allowed violations. +That way, the decision can be incorporated step-by-step. + +[[documenting]] +== Documenting your ADR results + +WARNING: To continue with this tutorial you have to change something in the code yourself! + +In the current state the execution of the maven build stops after analyzing the `adr:DeepLinking:NoDeepLinkingBetweenAggregates` constraint! + +=== Stop build failures on constraint violation + +There are a few ways to work around this. The following two are presented: + +1. <> of the constraint to `MINOR`. +2. <> to continue the build on a produced error. (preferred for this tutorial) + +==== Changing the severity +You can simply add `severity="minor"` to the constraint: + +[source,xml] +---- + + + + +---- + +From now on the constraint violation only produces a warning. + +==== Reconfigure jQAssistant to continue on error +You can simply add `continue-on-failure: true` to the `analyze/report` section of your `link:.jqassistant.yml[]`. Afterward it should look like this: + +[source, yaml] +---- +jqassistant: + plugins: + #<1> + - group-id: org.jqassistant.contrib.plugin + artifact-id: jqassistant-jmolecules-plugin + version: 1.4.0 + + analyze: + groups: + #<2> + - jmolecules-ddd:Default + #<3> + - Default + + report: + continue-on-failure: true +---- + +==== More Options +It's also possible to change the default severity for constraints or tell jQAssistant to not throw an error on violation of a rule with a high severity. To learn more about this check the https://jqassistant.github.io/jqassistant/doc/2.0.0/manual/#_yaml_files[jQAssistant Manual] + +=== Setting up Asciidoctor + +To render actual ADRs from adoc files, Asciidoctor is added to the project. Also the `jqassistant-asciidoctorj-extensions` needs to be added to Asciidoctor. This is needed later to process and include the results generated by jQAssistant: + +[[pom-asciidoc]] +[source,xml] +.link:pom.xml[pom.xml] enabling Asciidoctor +---- +include::pom.xml[tag=Asciidocplugin,indent=0] +---- +<1> defines the file type for our reports +<2> defines that Asciidoctor preserves the folder structure we created inside our `src/docs/asciidoc` directory +<3> defines where jQAssistant puts the jqassistant-report.xml (normally you do not have to change this attribute, because jQAssistant puts the file in the already specified location) +<4> `jqassistant-asciidoctorj-extensions` is added + +TIP: If you want to know more about the `asciidoctor-maven-plugin` and the available configuration attributes check the https://docs.asciidoctor.org/maven-tools/latest/plugin/goals/process-asciidoc/#configuration[Asciidoctor Maven plugin Documentation]. + +=== Visually representing ADRs + +Firstly the `link:src/docs/asciidoc/index.adoc[]` is created (`src/docs/asciidoc/` is the default location for asciidoctor to look for adoc files): + +[source,asciidoc] +.link:src/docs/asciidoc/index.adoc[index.adoc] +---- += Example project for Architecture Decision Records <1> + +== ADRs: +* <> <2> + +\include::adrs/001-Deep-Linking.adoc[] <3> +---- +<1> Translates to a level 1 heading (== translates to level 2 heading) +<2> creates a hyperlink to a different section of the adoc document. The anchor which is referenced here is shown in the next code snippet +<3> includes the content from `link:src/docs/asciidoc/adrs/001-Deep-Linking.adoc[001-Deep-Linking.adoc]` seamless into this file. + +Secondly the `link:src/docs/asciidoc/adrs/001-Deep-Linking.adoc[]` is created. It uses the basic structure of an ADR. + +[source,asciidoc] +.link:src/docs/asciidoc/adrs/001-Deep-Linking.adoc[001-Deep-Linking.adoc] +---- + [[ADR_001:DeepLinking]] // <1> + == 001 - No deep linking between aggregates + + === Status: Proposed // <2> + + === Context // <3> + ... + + === Decision // <4> + ... + + === Consequences // <5> + ... +include::jQAssistant:Rules[constraints="adr:NoDeepLinkingBetweenAggregates", leveloffset=+3] <6> +---- +<1> Anchor for referencing the ADR (as mentioned in the code snippet before) +<2> Status of the ADR +<3> Context section of the ADR +<4> Decision section of the ADR +<5> Consequences section of the ADR +<6> This is an include statement that will be replaced by a dynamic report about the jQAssistant analysis results. For this the `jqassistant-asciidoctorj-extensions` as shown in <> section is required. + +TIP: To learn more about the include statement (6) check out the `generate-reports` tutorial in the jqassistant-101 tutorials. + +NOTE: For the full example, please check out the link:src/docs/asciidoc/adrs/001-Deep-Linking.adoc[001-Deep-Linking ADR] directly. + +== Resources + +1. link:https://jqassistant.github.io/jqassistant/doc/2.0.0/manual/[jQAssistant user manual] +2. link:http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions[Architecture Decision Records by Michael Nygard] +3. https://github.com/jqassistant-tutorials/jqassistant-101/tree/master/ddd-plugin[jQAssistat 101 on Domain Driven Design] +//TODO: change link later +4. https://docs.asciidoctor.org/maven-tools/latest/plugin/goals/process-asciidoc[Asciidoctor Maven plugin Documentation] \ No newline at end of file diff --git a/architecture-decision-records/src/docs/asciidoc/adrs/001-Deep-Linking.adoc b/architecture-decision-records/src/docs/asciidoc/adrs/001-Deep-Linking.adoc new file mode 100644 index 0000000..322a829 --- /dev/null +++ b/architecture-decision-records/src/docs/asciidoc/adrs/001-Deep-Linking.adoc @@ -0,0 +1,20 @@ +[[ADR_001:DeepLinking]] +== 001 - No deep linking between aggregates + +=== Status: Proposed + +=== Context + +* Migration of the product catalog from relational to document-based database + +=== Decision + +* Document-based databases require modeling of aggregates +** Deep links between aggregates are not allowed + +=== Consequences + +* All entities must only be referenced from one aggregate root +* Existing deep linking between aggregates must be removed + +include::jQAssistant:Rules[constraints="adr:DeepLinking:NoDeepLinkingBetweenAggregates", leveloffset=+3] \ No newline at end of file diff --git a/architecture-decision-records/src/docs/asciidoc/index.adoc b/architecture-decision-records/src/docs/asciidoc/index.adoc new file mode 100644 index 0000000..8f09a31 --- /dev/null +++ b/architecture-decision-records/src/docs/asciidoc/index.adoc @@ -0,0 +1,6 @@ += Example project for Architecture Decision Records + +== ADRs: +* <> + +include::adrs/001-Deep-Linking.adoc[] \ No newline at end of file diff --git a/architecture-decision-records/src/main/java/your/company/adr/project/Category.java b/architecture-decision-records/src/main/java/your/company/adr/project/Category.java index 630f9f8..d86d0c6 100644 --- a/architecture-decision-records/src/main/java/your/company/adr/project/Category.java +++ b/architecture-decision-records/src/main/java/your/company/adr/project/Category.java @@ -1,10 +1,10 @@ package your.company.adr.project; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; import java.util.List; +import org.jmolecules.ddd.annotation.*; -@DDD.AggregateRoot +@AggregateRoot public class Category { private List productOptions; diff --git a/architecture-decision-records/src/main/java/your/company/adr/project/Product.java b/architecture-decision-records/src/main/java/your/company/adr/project/Product.java index 6003140..0b862bc 100644 --- a/architecture-decision-records/src/main/java/your/company/adr/project/Product.java +++ b/architecture-decision-records/src/main/java/your/company/adr/project/Product.java @@ -1,10 +1,9 @@ package your.company.adr.project; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; - import java.util.Map; +import org.jmolecules.ddd.annotation.*; -@DDD.AggregateRoot +@AggregateRoot public class Product { private Category category; diff --git a/architecture-decision-records/src/main/java/your/company/adr/project/ProductOption.java b/architecture-decision-records/src/main/java/your/company/adr/project/ProductOption.java index 30c803c..35e45ed 100644 --- a/architecture-decision-records/src/main/java/your/company/adr/project/ProductOption.java +++ b/architecture-decision-records/src/main/java/your/company/adr/project/ProductOption.java @@ -1,7 +1,7 @@ package your.company.adr.project; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.*; -@DDD.Entity +@Entity public class ProductOption { } diff --git a/architecture-decision-records/src/main/java/your/company/adr/project/ProductOptionValue.java b/architecture-decision-records/src/main/java/your/company/adr/project/ProductOptionValue.java index 230b2bf..f79468c 100644 --- a/architecture-decision-records/src/main/java/your/company/adr/project/ProductOptionValue.java +++ b/architecture-decision-records/src/main/java/your/company/adr/project/ProductOptionValue.java @@ -1,8 +1,8 @@ package your.company.adr.project; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.*; -@DDD.ValueObject +@ValueObject public class ProductOptionValue { private T value; diff --git a/ddd-plugin/includes/architecture_BoundedContextOverview.svg b/ddd-plugin/includes/architecture_BoundedContextOverview.svg deleted file mode 100644 index 95b2a32..0000000 --- a/ddd-plugin/includes/architecture_BoundedContextOverview.svg +++ /dev/null @@ -1,25 +0,0 @@ -«BoundedContext DDD»order«BoundedContext DDD»catalogDEFINES_DEPENDENCYDEPENDS_ON \ No newline at end of file diff --git a/ddd-plugin/index.adoc b/ddd-plugin/index.adoc deleted file mode 100644 index f77d9c2..0000000 --- a/ddd-plugin/index.adoc +++ /dev/null @@ -1,243 +0,0 @@ -:imagesdir: includes - -= Getting started with jQAssistant and Domain Driven Design -Stephan Pirnbaum - -:numbered: - -[.lead] -// tag::lead[] -Demonstrates how to map DDD concepts in the code to the graph and apply constraints using the DDD plugin, -// end::lead[] - -NOTE: This tutorial has been written for jQAssistant 1.7.0 - -== Prerequisites - -- Any Java application which implements (or shall implement) DDD concepts - -== 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 <> -- Checking the <> -- Using the <> -- Using the <> -- Definition of <> - -[[MavenProject]] -== 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`: - -[source,xml] -.pom.xml ----- -include::pom.xml[tag=plugin,indent=0] ----- - -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. -[source,xml] -.pom.xml ----- -include::pom.xml[tag=dependency,indent=0] ----- - -The Maven build can be triggered as usual on the command line: - ----- -mvn clean install ----- - -[[PreDefinedRules]] -== 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. ----- - -[[PreDefinedConcepts]] -== 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. - -[source,java] -.src/main/java/your/company/project/order/package-info.java ----- -include::src/main/java/your/company/project/order/package-info.java[] ----- - -Or directly by annotating a specific Java class as shown next. - -[source,java] -.src/main/java/your/company/project/order/OrderService.java ----- -include::src/main/java/your/company/project/order/OrderService.java[tags=dddType] ----- - -[[PreDefinedConstraints]] -== 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 - - -[[ProjectSpecificRules]] -== 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 -.... -include::jqassistant/architecture.adoc[tag=group,indent=0] -.... - -It defines a group `architecture:Default` which must be activated in the `pom.xml` file (see pom in section <> above). -This group contains all constraints and concepts matching the definition. - -=== 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 -.... -include::jqassistant/architecture.adoc[tag=aggregates] -.... - -The project can be built and verified by running the following command: - ----- -mvn clean install ----- - -=== 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.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 -.... -include::jqassistant/architecture.adoc[tag=boundedcontexts] -.... - -Finally the group `architecture:Default` must be activated in the `pom.xml` file (see pom in section <> above). - -The project can be built and verified by running the following command: - ----- -mvn clean install ----- - -The resulting component diagram will look as follows: - -image::architecture_BoundedContextOverview.svg[] - -== Resources - -1. link:tutorial.zip[ZIP archive including the application] -2. https://jqassistant.github.io/jqassistant/doc/1.6.0/index.html#_concepts_and_constraints_provided_by_the_spring_plugin[jQAssistant Spring Plugin documentation] -3. https://maven.apache.org[Apache Maven] diff --git a/ddd-plugin/jqassistant/architecture.adoc b/ddd-plugin/jqassistant/architecture.adoc deleted file mode 100644 index fe33cca..0000000 --- a/ddd-plugin/jqassistant/architecture.adoc +++ /dev/null @@ -1,35 +0,0 @@ -// tag::group[] -[[architecture:Default]] -[role=group,includesConstraints="architecture:*",includesConcepts="architecture:*"] -== Architecture Documentation -// end::group[] - -// tag::aggregates[] -=== 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 ----- -// end::aggregates[] - -// tag::boundedcontexts[] -=== 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 ----- -// end::boundedcontexts[] diff --git a/ddd-plugin/src/main/java/your/company/project/catalog/package-info.java b/ddd-plugin/src/main/java/your/company/project/catalog/package-info.java deleted file mode 100644 index d617ff3..0000000 --- a/ddd-plugin/src/main/java/your/company/project/catalog/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@DDD.BoundedContext(name = "catalog") -package your.company.project.catalog; - -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; \ No newline at end of file diff --git a/ddd-plugin/src/main/java/your/company/project/order/OrderItemPrice.java b/ddd-plugin/src/main/java/your/company/project/order/OrderItemPrice.java deleted file mode 100644 index f913718..0000000 --- a/ddd-plugin/src/main/java/your/company/project/order/OrderItemPrice.java +++ /dev/null @@ -1,22 +0,0 @@ -package your.company.project.order; - -import lombok.Getter; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; - -@Getter -@DDD.ValueObject -public class OrderItemPrice { - - private double originalPrice; - - private double discounPercentage; - - private double finalPrice; - - public OrderItemPrice(double originalPrice, double discounPercentage) { - this.originalPrice = originalPrice; - this.discounPercentage = discounPercentage; - this.finalPrice = originalPrice * (1 - discounPercentage); - } - -} diff --git a/ddd-plugin/src/main/java/your/company/project/order/OrderRepository.java b/ddd-plugin/src/main/java/your/company/project/order/OrderRepository.java deleted file mode 100644 index 1d1209f..0000000 --- a/ddd-plugin/src/main/java/your/company/project/order/OrderRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package your.company.project.order; - -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; - -@DDD.Repository -public interface OrderRepository { - - Order findById(long id); - - Order save(Order order); - -} diff --git a/ddd-plugin/src/main/java/your/company/project/order/package-info.java b/ddd-plugin/src/main/java/your/company/project/order/package-info.java deleted file mode 100644 index 7a4b01b..0000000 --- a/ddd-plugin/src/main/java/your/company/project/order/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@DDD.BoundedContext(name = "order", dependsOn = {"catalog"}) -package your.company.project.order; - -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; \ No newline at end of file diff --git a/generate-reports-about-structures-and-metrics/includes/metrics_csv_link.png b/generate-reports-about-structures-and-metrics/includes/metrics_csv_link.png deleted file mode 100644 index f5452eb..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/metrics_csv_link.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/includes/metrics_csv_sheet.png b/generate-reports-about-structures-and-metrics/includes/metrics_csv_sheet.png deleted file mode 100644 index 8c51c27..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/metrics_csv_sheet.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/includes/metrics_table.png b/generate-reports-about-structures-and-metrics/includes/metrics_table.png deleted file mode 100644 index 5d06575..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/metrics_table.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/includes/module_class_diagram.png b/generate-reports-about-structures-and-metrics/includes/module_class_diagram.png deleted file mode 100644 index 6a7b866..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/module_class_diagram.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/includes/module_component_diagram.png b/generate-reports-about-structures-and-metrics/includes/module_component_diagram.png deleted file mode 100644 index 5d7e46b..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/module_component_diagram.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/includes/module_graphml_report_link.png b/generate-reports-about-structures-and-metrics/includes/module_graphml_report_link.png deleted file mode 100644 index 57cfba8..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/module_graphml_report_link.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/includes/module_sequence_diagram.png b/generate-reports-about-structures-and-metrics/includes/module_sequence_diagram.png deleted file mode 100644 index 2cb0931..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/module_sequence_diagram.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/includes/module_table.png b/generate-reports-about-structures-and-metrics/includes/module_table.png deleted file mode 100644 index d9d7d88..0000000 Binary files a/generate-reports-about-structures-and-metrics/includes/module_table.png and /dev/null differ diff --git a/generate-reports-about-structures-and-metrics/index.adoc b/generate-reports-about-structures-and-metrics/index.adoc deleted file mode 100644 index bb62569..0000000 --- a/generate-reports-about-structures-and-metrics/index.adoc +++ /dev/null @@ -1,240 +0,0 @@ -:toc: left -:imagesdir: includes -= Generate Reports About Structures and Metrics -Dirk Mahler - -:numbered: - -[.lead] -// tag::lead[] -Generate HTML reports from Asciidoc documents including rules and embed results as tables, diagrams or CSV files. -// end::lead[] - -NOTE: This tutorial is written for version 1.8.0 of jQAssistant. - -== Overview - -Rules (i.e. concepts and constraints) may be embedded into Asciidoc files. -jQAssistant executes these rules and renders the documents to HTML with embedded results. - -By default the results are represented as tables but it is possible to specify different report types: - -`<>`:: - Nodes and relationships of the result are converted to a PlantUML component diagram and rendered to an SVG file. -`<>`:: - Nodes representing Java packages, classes, fields or members and their connecting relations are converted - to a PlantUML class diagram which is rendered to an SVG file. -`<>`:: - Path structures of the result are converted to a PlantUML sequence diagrams which is rendered to an SVG file. - -NOTE: For rendering https://plantuml.com/[PlantUML] diagrams http://www.graphviz.org[Graphviz] needs to be installed and the `dot` executable available via the system path. - -Furthermore a report type may be chosen for creating a file of a dedicated format which then will be shown as link: - -`<>`:: - Similar to the `plantuml-component-diagram` nodes and relationships are converted to a GraphML file. - This may be viewed and explored using http://www.yworks.com/en/products/yfiles/yed/[`yEd`]. -`<>`:: - Create a CSV file containing tabular data for further analysis in other tools. - -TIP: Usually concepts are used for creating reports as they represent your chosen design & architecture language. - -For this tutorial the following project structure is going to be used: - -[source,raw] ----- -your.project <1> -your.project.a <2> -your.project.b -your.project.c ----- - -<1> The root package of the project is `your.project`. -<2> A package located directly within the root package represents a `Module`. -The project consists of the modules `a`, `b` and `c` where `b` and `c` contain Java types depending on types located in `a`. - -The structure and the metrics of this project shall be continuously reported within each build. - -== Integrate jQAssistant Into The Build Process - -The tutorial uses https://maven.apache.org[Apache Maven] for building the project. - -jQAssistant is enabled by adding the plugin to the build/plugins section of the file `pom.xml`. - -.pom.xml -[source,xml] ----- -include::pom.xml[tag=plugin,indent=0] ----- - -TIP: To verify the setup a build should be triggered using `mvn verify`. -The build should succeed and show a console output should containing jQAssistant's scan and analysis messages. - -The rules are embedded in Asciidoc documents which are located in the folder `jqassistant/`: - -[source,raw] ------ -jqassistant/index.adoc <1> -jqassistant/module.adoc <2> -jqassistant/metrics.adoc <3> ------ -<1> link:jqassistant/index.adoc[index.adoc]: the document used for rendering by Asciidoctor, it contains include directives for both other documents. -<2> link:jqassistant/module.adoc[module.adoc]: reports about the project's module structure as tables, diagrams and GraphML files. -<3> link:jqassistant/metrics.adoc[metrics.adoc]: rules for collecting and reporting metrics as tables and CSV documents. - -TIP: The file `index.adoc` is picked up by jQAssistant by default for rendering to HTML. -It must contain at least one rule, usually this is a group declaration. -The rendered HTML documents are located in the folder `target/jqassistant/report/asciidoc`. - -== Structural Reports - -=== Tables - -The file `module.adoc` defines concepts related to the module structure of the project and for generating reports in different representations. - -A concept `module:Module` first adds a label `Module` to each child package of the root package `your.project`: - -.... -include::jqassistant/module.adoc[tags=moduleModule] -.... - -The returned result defines two columns `Module` (the name of the module) and `Package`. -As no report type is specified for this concept its result is rendered to a table containing these columns: - -image::module_table.png[Resultset] - -[[component-diagrams]] -=== Component Diagrams -The concept `module:Dependencies` which is based on `module:Module` aggregates the type dependencies to the module packages: - -.... -include::jqassistant/module.adoc[tags=moduleDependencies] -.... - -This concept returns the modules (module1, module2) and their dependency relations (dependsOn). -The result is used for generating a component diagram by setting the `reportType` attribute to `plantuml-component-diagram`. - -image::module_component_diagram.png[] - -TIP: The generated .plantuml and .svg files are located in the folder `target/jqassistant/report/plantuml`. - -[[graphml]] -=== GraphML Files - -If diagrams become more complex it may be useful to export them as GraphML documents. -This can be achieved by setting the report type `graphml` as illustrated by the concept `module:DependenciesGraphML`: - -.... -include::jqassistant/module.adoc[tags=moduleDependenciesGraphML] -.... - -The HTML document will provide a link to the GraphML file: - -image::module_graphml_report_link.png[Resultset] - -TIP: The generated .graphml files are located in the folder `target/jqassistant/report/graphml`. -They may be viewed using http://www.yworks.com/en/products/yfiles/yed/[yEd]. -After opening a file you need to apply a layout, Layout->Hierarchical (Alt-Shift-H). - -image::module_graphml_yed.png[] - -By extending the result structure the GraphML reports may be used to allow an interactive drill-down from module to type level: - -.... -include::jqassistant/module.adoc[tags=moduleDependenciesGraphMLDrilldown] -.... - -This concept returns a graph for each module which is rendered as `parent` containing types (`nodes`) and dependencies (`relationships`) per module. -The generated GraphML report file can be interactively explored in yEd by expanding (`+`) or collapsing (`-`) module nodes and applying the horizontal layout: - -image::module_graphml_drilldown_yed.png[] - -[[class-diagrams]] -=== Class Diagrams - -Java structures returned by a rule may be rendered to class diagrams. -The following elements are supported: - -* Packages (`:Java:Package`) -* Types (`:Java:Type`) -* Members (`:Java:Member`, `:Java:Field`, `:Java:Method`) -* Inheritance relations between types (`:EXTENDS`, `:IMPLEMENTS`) -* any other relations between types (rendered as associations) - -The concept `module:ClassDiagram` returns all types including their optional fields and methods: - -.... -include::jqassistant/module.adoc[tags=moduleClassDiagram] -.... - -As the report type is set to `plantuml-class-diagram` the following diagram is rendered and embedded into the HTML document: - -image::module_class_diagram.png[] - -[[sequence-diagrams]] -=== Sequence Diagrams - -A rule returning a path structure in a column named `sequence` can be rendered to sequence diagrams by setting the report type to `plantuml-sequence-diagram`: - -.... -include::jqassistant/module.adoc[tags=moduleSequenceDiagram] -.... - -The nodes of the sequence are interpreted as participants and the relationships between them as messages. -This results in the following diagram to be generated: - -image::module_sequence_diagram.png[] - -== Metrics - -=== Tables - -The results of concepts or constraints are rendered to tables by default. -This may be used to directly embed metrics into the HTML documents as illustrated by the concept `metrics:LoCAndComplexity` located in `metrics.adoc`: - -.... -include::jqassistant/metrics.adoc[tags=metricsTop10LoCAndCC] -.... - -The HTML document will contain the following table: - -image::metrics_table.png[] - -[[csv]] -=== CSV Reports - -It may be useful to provide metrics as a CSV files for further analysis in other tools. -Fur such cases the report type `csv` may be specified: - -.... -include::jqassistant/metrics.adoc[tags=metricsLoCTop10LoCAndCCAsCSV] -.... - -Instead of an embedded table a link to the generated CSV file will be rendered: - -image::metrics_csv_link.png[] - -TIP: Generated .csv files are located in the folder `target/jqassistant/report/csv`. - -The file can now be imported into tools providing support for CSV files: - -image::metrics_csv_sheet.png[] - -== Rules in XML files - -jQAssistant rules may be located in Asciidoc or XML files. -The latter are not rendered to HTML but the results of concepts and constraints can be reported in a similar way as described in the previous sections. -This can be achieved by adding a `` element with a `type` attribute: - -[source,xml] -.jqassistant/module.xml ----- -include::jqassistant/module.xml[] ----- - -The generated files will be located in the according sub-directories of `target/jqassistant/report`. - -== Resources - -1. link:tutorial.zip[ZIP archive including the application] - diff --git a/generate-reports-about-structures-and-metrics/jqassistant/index.adoc b/generate-reports-about-structures-and-metrics/jqassistant/index.adoc deleted file mode 100644 index ca3e915..0000000 --- a/generate-reports-about-structures-and-metrics/jqassistant/index.adoc +++ /dev/null @@ -1,14 +0,0 @@ -:toc: left -= My Project - -// tag::default[] -[[default]] -[role=group,includesGroups="module:Default,metrics:Default"] -== jQAssistant Rules - -This section describes that default rules that are executed during each build. -// end::default[] - -include::module.adoc[] -include::metrics.adoc[] - diff --git a/generate-reports-about-structures-and-metrics/jqassistant/metrics.adoc b/generate-reports-about-structures-and-metrics/jqassistant/metrics.adoc deleted file mode 100644 index 1955410..0000000 --- a/generate-reports-about-structures-and-metrics/jqassistant/metrics.adoc +++ /dev/null @@ -1,40 +0,0 @@ -// tag::metricsDefault[] -[[metrics:Default]] -[role=group,includesConcepts="metrics:*"] -== Metrics - -This section describes metrics of the project. -// end::metricsDefault[] - -// tag::metricsTop10LoCAndCC[] -[[metrics:Top10LoCAndCC]] -[source,cypher,role=concept] -.The top 10 types with the highest LoC (lines of code) and aggregated CC (cyclomatic complexity). ----- -MATCH - (:Artifact)-[:CONTAINS]->(type:Type)-[:DECLARES]->(method:Method) -RETURN - type AS Type, sum(method.cyclomaticComplexity) AS CC, sum(method.effectiveLineCount) AS LoC -ORDER BY - LoC DESC, CC DESC -LIMIT - 10 ----- -// end::metricsTop10LoCAndCC[] - -// tag::metricsLoCTop10LoCAndCCAsCSV[] -[[metrics:Top10LoCAndCCAsCSV]] -[source,cypher,role=concept,reportType="csv"] -.The top 10 types with the highest LoC (lines of code) and CC (cyclomatic complexity). ----- -MATCH - (:Artifact)-[:CONTAINS]->(type:Type)-[:DECLARES]->(method:Method) -RETURN - type AS Type, sum(method.cyclomaticComplexity) AS CC, sum(method.effectiveLineCount) AS LoC -ORDER BY - LoC DESC, CC DESC -LIMIT - 10 ----- -// end::metricsLoCTop10LoCAndCCAsCSV[] - diff --git a/generate-reports-about-structures-and-metrics/jqassistant/module.adoc b/generate-reports-about-structures-and-metrics/jqassistant/module.adoc deleted file mode 100644 index 3c602db..0000000 --- a/generate-reports-about-structures-and-metrics/jqassistant/module.adoc +++ /dev/null @@ -1,112 +0,0 @@ -// tag::moduleDefault[] -[[module:Default]] -[role=group,includesConcepts="module:*"] -== Module - -This section describes rules regarding modules and their dependencies. -// end::moduleDefault[] - -// tag::moduleModule[] -[[module:Module]] -[source,cypher,role=concept] -.All packages in the root package of the main artifact are labeled as `Module`. ----- -MATCH - (:Main:Artifact)-[:CONTAINS]->(root:Package)-[:CONTAINS]->(module:Package) -WHERE - root.fqn = "your.project" -SET - module:Module -RETURN - module.name as Module, module.fqn as Package -ORDER BY - module.name ----- -// end::moduleModule[] - -// tag::moduleDependencies[] -[[module:Dependencies]] -[source,cypher,role=concept,requiresConcepts="module:Module",reportType="plantuml-component-diagram"] -.A module depends on another module if there is a dependency of Java types between both. ----- -MATCH - (module1:Module)-[:CONTAINS*]->(t1:Type), - (module2:Module)-[:CONTAINS*]->(t2:Type), - (t1)-[:DEPENDS_ON]->(t2) -WHERE - module1 <> module2 -WITH - module1, module2, COUNT(*) AS weight -MERGE - (module1)-[dependsOn:DEPENDS_ON_MODULE]->(module2) -SET - dependsOn.weight = weight -RETURN - module1, dependsOn, module2 ----- -// end::moduleDependencies[] - -// tag::moduleDependenciesGraphML[] -[[module:DependenciesGraphML]] -[source,cypher,role=concept,requiresConcepts="module:Dependencies",reportType="graphml"] -.Modules and their dependencies as GraphML report. ----- -MATCH - (module:Module) -OPTIONAL MATCH - (module)-[dependsOn:DEPENDS_ON_MODULE]->(:Module) -RETURN - * ----- -// end::moduleDependenciesGraphML[] - -// tag::moduleDependenciesGraphMLDrilldown[] -[[module:DependenciesGraphMLDrilldown]] -[source,cypher,role=concept,requiresConcepts="module:Module",reportType="graphml"] -.Modules and their dependencies as GraphML report with drill-down to type level. ----- -MATCH - (module:Module)-[:CONTAINS*]->(type:Type) -OPTIONAL MATCH - (type)-[dependsOn:DEPENDS_ON]->(:Type)<-[:CONTAINS*]-(:Module) -RETURN - { - role: "graph", - parent: module, - nodes: collect(type), - relationships: collect(dependsOn) - } ----- -// end::moduleDependenciesGraphMLDrilldown[] - -// tag::moduleClassDiagram[] -[[module:ClassDiagram]] -[source,cypher,role=concept,requiresConcepts="module:Module",reportType="plantuml-class-diagram"] -.Class Diagram ----- -MATCH - (module:Module:Package)-[:CONTAINS*]->(type:Type) -OPTIONAL MATCH - (type)-[extends:EXTENDS|IMPLEMENTS]->(:Type) -OPTIONAL MATCH - (type)-[:DECLARES]->(field:Field) -OPTIONAL MATCH - (type)-[:DECLARES]->(method:Method) -RETURN - * ----- -// end::moduleClassDiagram[] - -// tag::moduleSequenceDiagram[] -[[module:SequenceDiagram]] -[source,cypher,role=concept,reportType="plantuml-sequence-diagram"] -.Sequence Diagram ----- -MATCH - (:Type{name:"ServiceCImpl"})-[:DECLARES]->(method:Method{name:"run"}), - sequence=((method)-[:INVOKES*]->(:Method)) -RETURN - sequence ----- -// end::moduleSequenceDiagram[] - diff --git a/generate-reports-about-structures-and-metrics/jqassistant/module.xml b/generate-reports-about-structures-and-metrics/jqassistant/module.xml deleted file mode 100644 index 9aa6f3a..0000000 --- a/generate-reports-about-structures-and-metrics/jqassistant/module.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - Modules and their dependencies as GraphML report. - (:Module) - RETURN - * - ]]> - - - - diff --git a/generate-reports-about-structures-and-metrics/pom.xml b/generate-reports-about-structures-and-metrics/pom.xml deleted file mode 100644 index 570d487..0000000 --- a/generate-reports-about-structures-and-metrics/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - 4.0.0 - - your.project - parent - - 1.0.0-SNAPSHOT - - - 3.5.0 - - - - - 1.8.0 - - - - - - com.buschmais.jqassistant - jqassistant-maven-plugin - ${jqassistant.version} - - - default - - scan - analyze - - - - - - - - - diff --git a/generate-reports/.jqassistant.yml b/generate-reports/.jqassistant.yml new file mode 100644 index 0000000..b985dbf --- /dev/null +++ b/generate-reports/.jqassistant.yml @@ -0,0 +1,15 @@ +jqassistant: + plugins: + #<1> + - group-id: org.jqassistant.plugin + artifact-id: jqassistant-plantuml-report-plugin + version: 2.0.1 + #<2> + - group-id: org.jqassistant.plugin + artifact-id: jqassistant-graphml-plugin + version: 2.0.1 + + analyze: + groups: + #<3> + - Default \ No newline at end of file diff --git a/generate-reports/includes/module_class_diagram.png b/generate-reports/includes/module_class_diagram.png new file mode 100644 index 0000000..90dccb0 Binary files /dev/null and b/generate-reports/includes/module_class_diagram.png differ diff --git a/generate-reports/includes/module_component_diagram.png b/generate-reports/includes/module_component_diagram.png new file mode 100644 index 0000000..f702a6c Binary files /dev/null and b/generate-reports/includes/module_component_diagram.png differ diff --git a/generate-reports-about-structures-and-metrics/includes/module_graphml_drilldown_yed.png b/generate-reports/includes/module_graphml_drilldown_yed.png similarity index 100% rename from generate-reports-about-structures-and-metrics/includes/module_graphml_drilldown_yed.png rename to generate-reports/includes/module_graphml_drilldown_yed.png diff --git a/generate-reports/includes/module_graphml_report_link.png b/generate-reports/includes/module_graphml_report_link.png new file mode 100644 index 0000000..5890bc0 Binary files /dev/null and b/generate-reports/includes/module_graphml_report_link.png differ diff --git a/generate-reports-about-structures-and-metrics/includes/module_graphml_yed.png b/generate-reports/includes/module_graphml_yed.png similarity index 100% rename from generate-reports-about-structures-and-metrics/includes/module_graphml_yed.png rename to generate-reports/includes/module_graphml_yed.png diff --git a/generate-reports/includes/module_sequence_diagram.png b/generate-reports/includes/module_sequence_diagram.png new file mode 100644 index 0000000..e0074a7 Binary files /dev/null and b/generate-reports/includes/module_sequence_diagram.png differ diff --git a/generate-reports/includes/module_table.png b/generate-reports/includes/module_table.png new file mode 100644 index 0000000..f172b0a Binary files /dev/null and b/generate-reports/includes/module_table.png differ diff --git a/generate-reports/jqassistant/default.xml b/generate-reports/jqassistant/default.xml new file mode 100644 index 0000000..b0a7651 --- /dev/null +++ b/generate-reports/jqassistant/default.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/generate-reports/jqassistant/module.xml b/generate-reports/jqassistant/module.xml new file mode 100644 index 0000000..cf743b0 --- /dev/null +++ b/generate-reports/jqassistant/module.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + All packages in the root package of the main artifact are labeled as `Module`. + + (root:Package)-[:CONTAINS]->(module:Package) + WHERE + root.fqn = "your.project" + SET + module:Module + RETURN + module.name as Module, module.fqn as Package + ORDER BY + module.name + ]]> + + + + + + + + A module depends on another module if there is a dependency of Java types between both. + + (t1:Type), + (module2:Module)-[:CONTAINS*]->(t2:Type), + (t1)-[:DEPENDS_ON]->(t2) + WHERE + module1 <> module2 + WITH + module1, module2, COUNT(*) AS weight + MERGE + (module1)-[dependsOn:DEPENDS_ON_MODULE]->(module2) + SET + dependsOn.weight = weight + RETURN + module1, dependsOn, module2 + ]]> + + + + + + + + + Modules and their dependencies as GraphML report. + + (:Module) + RETURN + * + ]]> + + + + + + + + + Modules and their dependencies as GraphML report with drill-down to type level. + + (type:Type) + OPTIONAL MATCH + (type)-[dependsOn:DEPENDS_ON]->(:Type)<-[:CONTAINS*]-(:Module) + RETURN + { + role: "graph", + parent: module, + nodes: collect(type), + relationships: collect(dependsOn) + } + ]]> + + + + + + + + + Class Diagram + + (type:Type) + OPTIONAL MATCH + (type)-[extends:EXTENDS|IMPLEMENTS]->(:Type) + OPTIONAL MATCH + (type)-[:DECLARES]->(field:Field) + OPTIONAL MATCH + (type)-[:DECLARES]->(method:Method) + RETURN + * + ]]> + + + + + + + + Sequence Diagram + + (method:Method{name:"run"}), + sequence=((method)-[:INVOKES*]->(:Method)) + RETURN + sequence + ]]> + + + + + + \ No newline at end of file diff --git a/generate-reports/pom.xml b/generate-reports/pom.xml new file mode 100644 index 0000000..f9d64de --- /dev/null +++ b/generate-reports/pom.xml @@ -0,0 +1,73 @@ + + + + 4.0.0 + + your.project + parent + + 1.0.0-SNAPSHOT + + + 3.5.0 + + + + + + + + com.buschmais.jqassistant + jqassistant-maven-plugin + 2.0.4 + + + default + + scan + analyze + + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 2.2.4 + + html5 + true + + ${project.build.directory}/jqassistant/jqassistant-report.xml + + images + + + + + output-html + verify + + process-asciidoc + + + + + + + org.jqassistant.tooling.asciidoctorj + jqassistant-asciidoctorj-extensions + 1.0.0 + + + + + + + + + diff --git a/generate-reports/readme.adoc b/generate-reports/readme.adoc new file mode 100644 index 0000000..f8e0f61 --- /dev/null +++ b/generate-reports/readme.adoc @@ -0,0 +1,291 @@ +:toc: left +:imagesdir: includes += Generate Reports About Structures and Metrics +Dirk Mahler + +:numbered: + +[.lead] +// tag::lead[] +This tutorial is about generating descriptive reports from your jqassistant-report.xml. This file gets produced by jQAssistant when building your project using jQAssistant. You can render full on documents including rules with their embedded result tables, diagrams or CSV files. +// end::lead[] + +NOTE: This tutorial is written for version 2.0.4 of jQAssistant. + +== Overview + +Rules (i.e. concepts, constraints and groups) are written in xml files. When executing, jQAssistant produces a jqassistant-report.xml file is produced which reflects the analysis results. You can then use Asciidoctorj in combination with the `jqassistant-asciidoctorj-extensions` plugin to transform this xml file to readable html, pdf, confluence or any other file type supported by Asciidoctorj. + +=== Report type options + +When writing the rules, you have the option to specify different report types to visualize the results of your rules. These are: + +`<>`:: + Nodes and relationships of the result are converted to a PlantUML component diagram and rendered to an SVG file. +`<>`:: + Nodes representing Java packages, classes, fields or members and their connecting relations are converted + to a PlantUML class diagram which is rendered to an SVG file. +`<>`:: + Path structures of the result are converted to a PlantUML sequence diagrams which is rendered to an SVG file. + +Furthermore a report type may be chosen for creating a file of a dedicated format which then will be shown as link: + +`<>`:: + Similar to the `plantuml-component-diagram` nodes and relationships are converted to a GraphML file. + This may be viewed and explored using http://www.yworks.com/en/products/yfiles/yed/[yEd]. +`csv`:: + Create a CSV file containing tabular data for further analysis in other tools. There is no section about this type in the tutorial, but it is generally very similar to the example in the <> section (the only difference being, that the results are put into a csv). + +WARNING: For some of these export types, you may need some extra plugins. Refer to the <> section for details. + +=== Result rendering + +When writing your documentation, you have the option to use the following include directives: + +`<>`:: +renders a summary table with the rules from the project. + +`<>`:: +renders the rules from the project with detailed information for each rule. + +=== Project structure + +For this tutorial the following project structure is going to be used: + +[source,raw] +---- +your.project <1> +your.project.a <2> +your.project.b +your.project.c +---- + +<1> The root package of the project is `your.project`. +<2> A package located directly within the root package represents a `Module`. +The project consists of the modules `a`, `b` and `c` where `b` and `c` contain Java types depending on types located in `a`. + +== Integrate required dependencies into the project + +The tutorial uses https://maven.apache.org[Apache Maven] for building the project. + +=== Adding jQAssistant + +jQAssistant is enabled by adding the plugin to the build/plugins section of the file `pom.xml`: + +[source,xml] +.pom.xml enabling jQAssistant +---- +include::pom.xml[tag=jQAplugin,indent=0] +---- + +=== Adding Asciidoctor + +To render the reports as readable files we need to implement Asciidoctor into our project. In our example we use the `asciidoctor-maven-plugin`. We also need to add the `jqassistant-asciidoctorj-extensions` to Asciidoctor. This is needed later to process and include the results generated by jQAssistant. The following snippet also stems from the build/plugins part of the pom.xml: + +[source,xml] +.pom.xml enabling Asciidoctor +---- +include::pom.xml[tag=Asciidocplugin,indent=0] +---- +<1> defines the file type for our reports +<2> defines that Asciidoctor preserves the folder structure we created inside our `src/docs/asciidoc` directory +<3> defines where jQAssistant puts the jqassistant-report.xml (jQAssistant normally puts the file always in this location; if you want to process another `jqassistant-report.xml` you have lying around somewhere, you can change this attribute) +<4> defines where the `jqassistant-asciidoctorj-extensions` copies the images referenced by the `jqassistant-report.xml` to (the path is relative to the export path) +<5> includes the aforementioned `jqassistant-asciidoctorj-extensions` into the Asciidoctor chain + +TIP: If you want to know more about the `asciidoctor-maven-plugin` and the available configuration attributes check the https://github.com/asciidoctor/asciidoctor-maven-plugin#examples[Github page] for some helpful examples. + +=== Adding jQAssistant plugins + +To render your reports as diagrams or graphs you need to add some plugins to jQAssistant. Also you need to add the : + +[source, yaml] +.jqassistant.yml +.... +include::.jqassistant.yml[] +.... +<1> This plugin is needed to use the report types `plantuml-component-diagram`, `plantuml-class-diagram` and `plantuml-sequence-diagram` +<2> This plugin is needed to use the report type graphml +<3> This line tells jQAssistant to apply the group (groups are rules just like concepts and constraints) `Default`, which is defined in default.xml. + +TIP: To verify the setup, a build can be triggered using `mvn verify`. +The build should succeed and show a console output containing jQAssistant's scan and analysis messages. + +== Writing rules + +=== Where are the rules located? + +All rules are written as xml file which are located in the folder `jqassistant/`: + +[source,raw] +----- +jqassistant/default.xml <1> +jqassistant/module.xml <2> +----- +<1> link:jqassistant/default.xml[default.xml]: This file defines a top level group that (recursively) includes the rules from module.xml +<2> link:jqassistant/module.xml[module.xml]: This file defines rules that generate reports about the project's module structure as tables, diagrams and GraphML files. + +=== Rule-report types + +In the following sections the different report types for rules will be explained. The rules are processed by jQAssistant and the reports are be placed inside the `jqassistant-report.xml` (in case of rendered diagrams or attachments, the files are placed inside the `target/jqassistant/report` directory and are only referenced in the `jqassistant-report.xml`). + +In the following sections, all depicted pictures are generated by applying the steps from <>. + +==== Tables + +The file `module.xml` defines concepts related to the module structure of the project and for generating reports in different representations. + +A concept `module:Module` first adds a label `Module` to each child package of the root package `your.project`: + +[source,xml] +.... +include::jqassistant/module.xml[tags=moduleModule] +.... + +The returned result defines two columns `Module` (the name of the module) and `Package`. +As no report type is specified for this concept its result would be rendered to a table containing these columns: + +image::module_table.png[Resultset] + +NOTE: The table is not saved as a file. Instead, this tables structure is stored directly in the xml. To render it to a readable format, check the <> section. + +[[component-diagrams]] +==== Component Diagrams +The concept `module:Dependencies` which is based on `module:Module` aggregates the type dependencies to the module packages: + +[source,xml] +.... +include::jqassistant/module.xml[tags=moduleDependencies] +.... + +This concept returns the modules (module1, module2) and their dependency relations (dependsOn). +The result is used for generating a component diagram by setting the `reportType` attribute to `plantuml-component-diagram`. + +image::module_component_diagram.png[] + +TIP: The generated .plantuml and .svg files are located in the folder `target/jqassistant/report/plantuml`. + +[[graphml]] +==== GraphML Files + +If diagrams become more complex it may be useful to export them as GraphML documents: + +[source,xml] +.... +include::jqassistant/module.xml[tags=moduleDependenciesGraphML] +.... + +To render a graphml we are setting the `report type` to `graphml` as illustrated above. + +The HTML document will provide a link to the GraphML file: + +image::module_graphml_report_link.png[Resultset] + +TIP: The generated .graphml files are located in the folder `target/jqassistant/report/graphml`. +They may be viewed using http://www.yworks.com/en/products/yfiles/yed/[yEd]. +After opening a file you need to apply a layout, Layout->Hierarchical (Alt-Shift-H). + +//image::module_graphml_yed.png[] + +By extending the result structure the GraphML reports may be used to allow an interactive drill-down from module to type level: + +[source,xml] +.... +include::jqassistant/module.xml[tags=moduleDependenciesGraphMLDrilldown] +.... + +This concept returns a graph for each module which is rendered as `parent` containing types (`nodes`) and dependencies (`relationships`) per module. +The generated GraphML report file can be interactively explored in yEd by expanding (`+`) or collapsing (`-`) module nodes and applying the horizontal layout: + +//image::module_graphml_drilldown_yed.png[] + +[[class-diagrams]] +==== Class Diagrams + +Java structures returned by a rule may be rendered to class diagrams. +The following elements are supported: + +* Packages (`:Java:Package`) +* Types (`:Java:Type`) +* Members (`:Java:Member`, `:Java:Field`, `:Java:Method`) +* Inheritance relations between types (`:EXTENDS`, `:IMPLEMENTS`) +* any other relations between types (rendered as associations) + +The concept `module:ClassDiagram` returns all types including their optional fields and methods: + +[source,xml] +.... +include::jqassistant/module.xml[tags=moduleClassDiagram] +.... + +As the report type is set to `plantuml-class-diagram`, the following diagram is rendered: + +image::module_class_diagram.png[] + +[[sequence-diagrams]] +==== Sequence Diagrams + +The concept `module:SequenceDiagram` returns a path structure with a column named `sequence`. When there is such a column, the report can be rendered as a sequence diagram: + +[source,xml] +.... +include::jqassistant/module.xml[tags=moduleSequenceDiagram] +.... + +As the report type is set to `plantuml-sequence-diagram`, the following diagram is rendered: + +image::module_sequence_diagram.png[] + + +In this diagram the nodes of the sequence are interpreted as participants and the relationships between them as messages. + +== Rendering reports + +In this tutorial there are two files that will be rendered by Asciidoctor. The sources of both files are located in the `src/docs/asciidoc/modules` directory. This is the default location the `asciidoctor-maven-plugin` looks for files to render in. The rendered files are then stored in the `target/generated-docs` directory (also the default). + +TIP: Check the https://github.com/asciidoctor/asciidoctor-maven-plugin[asciidoctor-maven-plugin Github page] for more information on how to change these default values. + +In our example the link:src/docs/asciidoc/index.adoc[index.adoc] represents the root file. It includes the link:src/docs/asciidoc/modules/module.adoc[module.adoc] with the following line: + +.... +\include::modules/module.adoc[leveloffset=+1] +.... + +TIP: The `leveloffset` attribute is responsible for offsetting each heading from the link:src/docs/asciidoc/modules/module.adoc[module.adoc] by the given number. Refer to https://docs.asciidoctor.org/asciidoc/latest/directives/include/#include-syntax[include directive Asciidoctor] + +=== Rendering summaries + +To render a summary of our jQAssistant reports within an adoc file, we use: + +[source,adoc] +.... +\include::jQAssistant:Summary[] +.... + +This syntax is used in the link:src/docs/asciidoc/index.adoc[index.adoc]. + +NOTE: The summary table will be displayed in the index.html inside the `target/generated-docs` directory after you executed your maven build (`mvn verify`). + +=== Rendering rules + +To render a rules with their respective results within an adoc file, we use: + +[source,adoc] +.... +\include::jQAssistant:Rules[] +.... + +This syntax is used in the link:src/docs/asciidoc/modules/module.adoc[module.adoc]. + +TIP: The `concepts="module:Module"` attribute describes a way of filtering the displayed rules. You can use `concepts` and `constraints` attributes to specify the rules that are displayed (this also works for the summary). There is also the possibility to use wildcards (*) in these filters. Refer to the https://github.com/jqassistant-tooling/jqassistant-asciidoctorj-extensions#create-your-report[jqassistant-asciidoctorj-extensions readme] for more information. + +== Resources + +1. https://maven.apache.org[Apache Maven] +2. https://jqassistant.github.io/jqassistant/doc/2.0.0/manual[jQAssistant manual] +3. https://github.com/asciidoctor/asciidoctor-maven-plugin[asciidoctor-maven-plugin] +4. https://github.com/jqassistant-tooling/jqassistant-asciidoctorj-extensions[jqassistant-asciidoctorj-extensions] +5. http://www.yworks.com/en/products/yfiles/yed/[yEd] + + + diff --git a/generate-reports/src/docs/asciidoc/index.adoc b/generate-reports/src/docs/asciidoc/index.adoc new file mode 100644 index 0000000..903d2f6 --- /dev/null +++ b/generate-reports/src/docs/asciidoc/index.adoc @@ -0,0 +1,12 @@ +:toc: left += My Project + +Here will be displayed a summary, which summarizes all your rules in one table as described in the `readme.adoc` file in section "4.1 Rendering summaries": + +// tag::summary[] +include::jQAssistant:Summary[] +// end::summary[] + +The following part is an include of the module.adoc's content. + +include::modules/module.adoc[leveloffset=+1] \ No newline at end of file diff --git a/generate-reports/src/docs/asciidoc/modules/module.adoc b/generate-reports/src/docs/asciidoc/modules/module.adoc new file mode 100644 index 0000000..a8254c9 --- /dev/null +++ b/generate-reports/src/docs/asciidoc/modules/module.adoc @@ -0,0 +1,31 @@ += Module + +This section displays rules from the module.xml. + +== Tables +This is the table report described in the `readme.adoc` file in section "3.2.1 Tables": + +include::jQAssistant:Rules[concepts="module:Module", leveloffset=+2] + +== Component Diagrams +This is the Component Diagram result described in the `readme.adoc` file in section "3.2.2 Component Diagrams": + +include::jQAssistant:Rules[concepts="module:Dependencies", leveloffset=+2] + + +== GraphML Files +This is the GraphML File result described in the `readme.adoc` file in section "3.2.3 GraphML Files": + +include::jQAssistant:Rules[concepts="module:DependenciesGraphML", leveloffset=+2] + + +== Class Diagrams +This is the Class Diagram result described in the `readme.adoc` file in section "3.2.4 Class Diagrams": + +include::jQAssistant:Rules[concepts="module:ClassDiagram", leveloffset=+2] + + +== Sequence Diagrams +This is the Sequence Diagram result described in the `readme.adoc` file in section "3.2.5 Sequence Diagrams": + +include::jQAssistant:Rules[concepts="module:SequenceDiagram", leveloffset=+2] \ No newline at end of file diff --git a/generate-reports-about-structures-and-metrics/src/main/java/your/project/a/api/ServiceA.java b/generate-reports/src/main/java/your/project/a/api/ServiceA.java similarity index 100% rename from generate-reports-about-structures-and-metrics/src/main/java/your/project/a/api/ServiceA.java rename to generate-reports/src/main/java/your/project/a/api/ServiceA.java diff --git a/generate-reports-about-structures-and-metrics/src/main/java/your/project/a/impl/ServiceAImpl.java b/generate-reports/src/main/java/your/project/a/impl/ServiceAImpl.java similarity index 100% rename from generate-reports-about-structures-and-metrics/src/main/java/your/project/a/impl/ServiceAImpl.java rename to generate-reports/src/main/java/your/project/a/impl/ServiceAImpl.java diff --git a/generate-reports-about-structures-and-metrics/src/main/java/your/project/b/api/ServiceB.java b/generate-reports/src/main/java/your/project/b/api/ServiceB.java similarity index 100% rename from generate-reports-about-structures-and-metrics/src/main/java/your/project/b/api/ServiceB.java rename to generate-reports/src/main/java/your/project/b/api/ServiceB.java diff --git a/generate-reports-about-structures-and-metrics/src/main/java/your/project/b/impl/ServiceBImpl.java b/generate-reports/src/main/java/your/project/b/impl/ServiceBImpl.java similarity index 100% rename from generate-reports-about-structures-and-metrics/src/main/java/your/project/b/impl/ServiceBImpl.java rename to generate-reports/src/main/java/your/project/b/impl/ServiceBImpl.java diff --git a/generate-reports-about-structures-and-metrics/src/main/java/your/project/c/impl/ServiceCImpl.java b/generate-reports/src/main/java/your/project/c/impl/ServiceCImpl.java similarity index 100% rename from generate-reports-about-structures-and-metrics/src/main/java/your/project/c/impl/ServiceCImpl.java rename to generate-reports/src/main/java/your/project/c/impl/ServiceCImpl.java diff --git a/generate-reports-about-structures-and-metrics/verify.bsh b/generate-reports/verify.bsh similarity index 100% rename from generate-reports-about-structures-and-metrics/verify.bsh rename to generate-reports/verify.bsh diff --git a/ddd-plugin/.gitignore b/jmolecules-ddd/.gitignore similarity index 100% rename from ddd-plugin/.gitignore rename to jmolecules-ddd/.gitignore diff --git a/jmolecules-ddd/.jqassistant.yml b/jmolecules-ddd/.jqassistant.yml new file mode 100644 index 0000000..6cfada3 --- /dev/null +++ b/jmolecules-ddd/.jqassistant.yml @@ -0,0 +1,28 @@ +jqassistant: + plugins: + #<1> + - group-id: org.jqassistant.plugin + artifact-id: jqassistant-jmolecules-plugin + version: 2.0.0 + #<2> + - group-id: org.jqassistant.plugin + artifact-id: jqassistant-context-mapper-plugin + version: 2.0.0 + #<3> + - group-id: org.jqassistant.plugin + artifact-id: jqassistant-plantuml-report-plugin + version: 2.0.1 + + scan: + include: + files: + #<4> + - jqassistant/context-mapper/context-map.cml + + analyze: + groups: + #<5> + - jmolecules-ddd:Strict + #<6> + - architecture:Default + diff --git a/ddd-plugin/.mvn/wrapper/maven-wrapper.jar b/jmolecules-ddd/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from ddd-plugin/.mvn/wrapper/maven-wrapper.jar rename to jmolecules-ddd/.mvn/wrapper/maven-wrapper.jar diff --git a/ddd-plugin/.mvn/wrapper/maven-wrapper.properties b/jmolecules-ddd/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from ddd-plugin/.mvn/wrapper/maven-wrapper.properties rename to jmolecules-ddd/.mvn/wrapper/maven-wrapper.properties diff --git a/jmolecules-ddd/includes/architecture_BoundedContextOverview.svg b/jmolecules-ddd/includes/architecture_BoundedContextOverview.svg new file mode 100644 index 0000000..2e05482 --- /dev/null +++ b/jmolecules-ddd/includes/architecture_BoundedContextOverview.svg @@ -0,0 +1,24 @@ +«BoundedContext DDD JMolecules»catalog«BoundedContext DDD JMolecules»orderdefines dependencydepends on (7) \ No newline at end of file diff --git a/jmolecules-ddd/jqassistant/architecture.xml b/jmolecules-ddd/jqassistant/architecture.xml new file mode 100644 index 0000000..4f1a708 --- /dev/null +++ b/jmolecules-ddd/jqassistant/architecture.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + Only aggregates are allowed to be returned by repositories. + + (:Method)-[:RETURNS]->(t:Type) + WHERE + NOT t:JMolecules:DDD:AggregateRoot + RETURN + repo.fqn as Repository, t.fqn as IllegalReturnType + ]]> + + + + + + + Documentation of existing bounded contexts and their dependencies. + + (bc2) + WHERE + bc1 <> bc2 + RETURN + bc1, d, bc2 + ]]> + + + + + + \ No newline at end of file diff --git a/jmolecules-ddd/jqassistant/context-mapper/context-map.cml b/jmolecules-ddd/jqassistant/context-mapper/context-map.cml new file mode 100644 index 0000000..6dd7d90 --- /dev/null +++ b/jmolecules-ddd/jqassistant/context-mapper/context-map.cml @@ -0,0 +1,12 @@ +ContextMap Project { + type = SYSTEM_LANDSCAPE + state = TO_BE + + contains order + contains catalog + + catalog -> order +} + +BoundedContext order { } +BoundedContext catalog { } \ No newline at end of file diff --git a/ddd-plugin/mvnw b/jmolecules-ddd/mvnw similarity index 100% rename from ddd-plugin/mvnw rename to jmolecules-ddd/mvnw diff --git a/ddd-plugin/mvnw.cmd b/jmolecules-ddd/mvnw.cmd similarity index 100% rename from ddd-plugin/mvnw.cmd rename to jmolecules-ddd/mvnw.cmd diff --git a/ddd-plugin/pom.xml b/jmolecules-ddd/pom.xml similarity index 50% rename from ddd-plugin/pom.xml rename to jmolecules-ddd/pom.xml index fb67cc9..ce1d335 100644 --- a/ddd-plugin/pom.xml +++ b/jmolecules-ddd/pom.xml @@ -9,24 +9,22 @@ jar project - Demo project for the DDD Plug In + Demo project for the jMolecules Plugin UTF-8 UTF-8 - 1.7.0 - 1.8 + 1.11 - + - org.jqassistant.contrib.plugin - jqassistant-java-ddd-plugin - ${jqassistant.version} - provided + org.jmolecules + jmolecules-ddd + 1.6.0 - + org.projectlombok lombok @@ -37,46 +35,59 @@ - + com.buschmais.jqassistant jqassistant-maven-plugin - ${jqassistant.version} + 2.0.4 - default-cli scan analyze - - - java-ddd:Default - architecture:Default - - + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 2.2.4 + + html5 + true + + ${project.build.directory}/jqassistant/jqassistant-report.xml + + + + + + output-html + verify + + process-asciidoc + + - org.jqassistant.contrib.plugin - jqassistant-java-ddd-plugin - ${jqassistant.version} - - - org.jqassistant.contrib.plugin - jqassistant-asciidoc-report-plugin - ${jqassistant.version} + org.jqassistant.tooling.asciidoctorj + jqassistant-asciidoctorj-extensions + 1.0.0 - + org.apache.maven.plugins maven-compiler-plugin - 8 - 8 + 11 + 11 diff --git a/jmolecules-ddd/readme.adoc b/jmolecules-ddd/readme.adoc new file mode 100644 index 0000000..589ec26 --- /dev/null +++ b/jmolecules-ddd/readme.adoc @@ -0,0 +1,330 @@ +:imagesdir: includes + += Getting started with jQAssistant and Domain Driven Design +Stephan Pirnbaum + +:numbered: + +[.lead] +// tag::lead[] +This tutorial demonstrates how to map DDD (Domain Driven Design) concepts from the code to the jQAssistant-graph and apply constraints using the `jqassistant-jmolecules-plugin` plugin. +// end::lead[] + +NOTE: This tutorial has been written for jQAssistant 2.0.4 + +== Prerequisites + +- Any Java application which implements (or shall implement) DDD concepts + +== Overview + +The `jmolecules-ddd` dependency provides the ability to annotate the source code with well-known DDD concepts. In combination with the `jqassistant-jmolecules-plugin` for jQAssistant it's possible to generate an enriched jQAssistant-graph that directly uses these annotations. +This 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 the build if required. + +The steps in this tutorial illustrate: + +- Setup of the <> +- Show all <> +- <> +- <> +- Definition of <> + +[[MavenProject]] +== Setting up jQAssistant + +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`: + +[source,xml] +.link:pom.xml[] jQA integration +---- +include::pom.xml[tag=jQAplugin,indent=0] +---- + +The configuration above activates the goals `scan` and `analyze` during a build. + +Furthermore, to make use of the jmolecules annotations, the `jmolecules-ddd` plugin must be added as a maven dependency to the project as shown below. +[source,xml] +.link:pom.xml[] jmolecules dependency +---- +include::pom.xml[tag=jMolecules,indent=0] +---- + +At last some additional configurations need to be made in the link:.jqassistant.yml[]: + +[[yml]] +[source,yml] +.link:.jqassistant.yml[] +---- +include::.jqassistant.yml[] +---- +<1> Ads the `jqassistant-jmolecules-plugin` to jQAssistant. +<2> Ads the `jqassistant-context-mapper-plugin` to jQAssistant. (It's required for the section about the <>) +<3> Ads the `jqassistant-plantuml-report-plugin` to jQAssistant. (It's required for creating picture reports like in the section <>) +<4> Defines where the `jqassistant-context-mapper-plugin` finds the cml files. (It's required for the section about the <>) +<5> enables the `jmolecules-ddd:Strict` group. The `jmolecules-ddd:Strict` group contains all the rules from `jmolecules-ddd:Default` and some additional ones. Both include pre-defined rules of the `jqassistant-jmolecules-plugin`. They are used in sections <> and <>. +<6> enables the `architecture:Default` group (user-defined rules; used in section <>) + +The Maven build can be triggered as usual on the command line: + +---- +mvn clean verify +---- + +[[PreDefinedRules]] +== Checking active Rules + +The setup above activates (among others) the pre-defined group `jmolecules-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: + + +NOTE: The following console output reflects the case in which only the `jmolecules-ddd:Default` group is activated. If you execute this command on your own, some more rules will be displayed. + +---- +[INFO] Groups [1] +[INFO] "jmolecules-ddd:Default" +[INFO] Constraints [5] +[INFO] "jmolecules-ddd:MutableEntityId" - Checks that the id field of an entity (identifiable) is only manipulated via the constructor. +[INFO] "jmolecules-ddd:MutableValueObject" - Checks that fields of a value object are only manipulated via the constructor. The check includes fields from super types. +[INFO] "jmolecules-ddd:TypeInMultipleBoundedContexts" - Checks that a single DDD type is only part of one bounded context. +[INFO] "jmolecules-ddd:TypeInMultipleModules" - Checks that a single DDD type is only part of one module. +[INFO] "jmolecules-ddd:ValueObjectReferencingEntityOrAggregateRoot" - Checks that a :ValueObject does not reference :Entity and :AggregateRoot nodes (identifiable). This check includes super types. +[INFO] Concepts [20] +[INFO] "java:MemberInheritedFrom" - Creates a relationship INHERITS between two "Member" labeled nodes if a member is inherited from a + super type. +[INFO] "java:MethodOverrides" - Creates a relationship OVERRIDES between two "Method" labeled nodes if a method overrides another + one from a super type. +[INFO] "jmolecules-ddd:AggregateRootExtend" - Labels all Java types which extend from + org.jmolecules.ddd.types.AggregateRoot as :JMolecules:DDD:AggregateRoot:Entity:Identifiable. +[INFO] "jmolecules-ddd:AggregateRootType" - Labels all Java types which are annotated by + org.jmolecules.ddd.annotation.AggregateRoot as :JMolecules:DDD:AggregateRoot:Entity:Identifiable. +[INFO] "jmolecules-ddd:BoundedContextDependency" - Propagates the dependencies between Types of different Bounded Contexts to the level of Bounded + Contexts including an aggregated weight. +[INFO] "jmolecules-ddd:BoundedContextPackage" - Maps all Java types which are located in a package annotated by + org.jmolecules.ddd.annotation.BoundedContext to the corresponding BoundedContext node. +[INFO] "jmolecules-ddd:EntityExtend" - Labels all Java types which extend from org.jmolecules.ddd.annotation.Entity as + :JMolecules:DDD:Entity:Identifiable. +[INFO] "jmolecules-ddd:EntityType" - Labels all Java types which are annotated by org.jmolecules.ddd.annotation.Entity as + :JMolecules:DDD:Entity:Identifiable. +[INFO] "jmolecules-ddd:FactoryType" - Labels all Java types which are annotated by + org.jmolecules.ddd.annotation.Factory as :JMolecules:DDD:Factory. +[INFO] "jmolecules-ddd:IdentifiedByAnnotation" - Merges a :HAS_IDENTITY relation for each member that is annotated by + org.jmolecules.ddd.annotation.Identity between the defining type and the member and marks the member as + :JMolecules:DDD:Identity. +[INFO] "jmolecules-ddd:IdentifiedByMethod" - Merges a :HAS_IDENTITY relation between the defining type and the 'getId' method when the type + extends from either 'AggregateRoot' or 'Entity' and marks the method as :JMolecules:DDD:Identity. +[INFO] "jmolecules-ddd:IdentifiedBySuperClass" - Transfers the :HAS_IDENTITY relation from a base class to its implementing classes. +[INFO] "jmolecules-ddd:IdentifierExtend" - Labels all Java types which extend from org.jmolecules.ddd.types.Identifier as + :JMolecules:DDD:Identifier. +[INFO] "jmolecules-ddd:ModuleDependency" - Propagates the dependencies between Types of different Modules to the level of Module including an + aggregated weight. +[INFO] "jmolecules-ddd:ModulePackage" - Maps all Java types which are located in a package annotated by + org.jmolecules.ddd.annotation.Module to the corresponding Module node. +[INFO] "jmolecules-ddd:RepositoryExtend" - Labels all Java types which extend from + org.jmolecules.ddd.types.Repository as :JMolecules:DDD:Repository. +[INFO] "jmolecules-ddd:RepositoryType" - Labels all Java types which are annotated by + org.jmolecules.ddd.annotation.Repository as :JMolecules:DDD:Repository. +[INFO] "jmolecules-ddd:ServiceType" - Labels all Java types which are annotated by + org.jmolecules.ddd.annotation.Service as :JMolecules:DDD:Service. +[INFO] "jmolecules-ddd:ValueObjectExtend" - Labels all Java types which extend from org.jmolecules.ddd.types.ValueObject as + :JMolecules:DDD:ValueObject. +[INFO] "jmolecules-ddd:ValueObjectType" - Labels all Java types which are annotated by + org.jmolecules.ddd.annotation.ValueObject as :JMolecules:DDD:ValueObject. +---- + +[[jmdddDefault]] +== Using jmolecules-ddd:Default + +=== Pre-Defined Concepts +The `jqassistant-jmolecules-plugin` comes with pre-defined jQAssistant concepts. +For each DDD notion, 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. + +[source,java] +.link:src/main/java/your/company/project/order/package-info.java[] +---- +include::src/main/java/your/company/project/order/package-info.java[] +---- + +Or directly by annotating a specific Java class as shown next. + +[source,java] +.link:src/main/java/your/company/project/order/OrderService.java[] +---- +include::src/main/java/your/company/project/order/OrderService.java[tags=dddType] +---- + +[source,java] +.link:src/main/java/your/company/project/order/OrderRepository.java[] +---- +include::src/main/java/your/company/project/order/OrderRepository.java[tags=dddType] +---- + +[source,java] +.link:src/main/java/your/company/project/catalog/Product.java[] +---- +include::src/main/java/your/company/project/catalog/Product.java[tags=dddType] +---- + +NOTE: In all cases you need to import these annotations via `import org.jmolecules.ddd.annotation.*` to your files. + +TIP: There are even more possible annotations. Check the directories in `src/main/java/your/company/project/` for more examples. Also check link:https://github.com/jqassistant-plugin/jqassistant-jmolecules-plugin#concepts-for-domain-driven-design[jmolecules jQAssistant plugin] for more supported annotations and link:https://github.com/xmolecules/jmolecules/blob/main/readme.adoc[jmolecules-ddd readme] for general use of `jmolecules-ddd` annotations + +When executing the `jmolecules-ddd:Default` group several concepts will be applied, that map the annotated ddd notions to the jQAssistant graph. + +=== Pre-Defined Constraints + +The applied Concepts enable the execution of several constraints (also provided by `jmolecules-ddd:Default`). These check for basic architecture violations. +The following things will be checked during build time: + +* jmolecules-ddd:MutableEntityId +** Checks that the id field of an entity is only manipulated via the constructor +* jmolecules-ddd:MutableValueObject +** Checks that fields of a ValueObject are only manipulated by the constructor (including fields inherited from super types) +* jmolecules-ddd:TypeInMultipleBoundedContexts +** Checks that a single DDD type is only part of one bounded context +* jmolecules-ddd:TypeInMultipleModules +** Checks that a single DDD type is only part of one module. +* jmolecules-ddd:ValueObjectReferencingEntityOrAggregateRoot +** Checks that a ValueObject does not reference an Entity or AggregateRoot (including super types) + +[[jmdddStrict]] +== Using jmolecules-ddd:Strict + +Just like `jmolecules-ddd:Default`, `jmolecules-ddd:Strict` provides some rules for basic ddd validation. Like the name implies, these rules are a bit stricter and may not be applicable for all cases (hence they can be enabled on their own; if you do not want to use them, change the `jmolecules-ddd:Strict` group to `jmolecules-ddd:Default` (see section <>)). + +Most importantly `jmolecules-ddd:Strict` contains 3 additional constraints: + +* jmolecules-ddd:NonFinalFieldInValueObject +** Reports all fields inside ValueObjects that are not final (including fields inherited from super types) +* jmolecules-ddd:NonFinalEntityId +** Checks that the id field of an entity is final. +* jmolecules-ddd:IllegalDependenciesBetweenBoundedContexts +** Checks that dependencies between BoundedContexts exist only where allowed. + +For the third constraint it's necessary to tell jQAssistant, which dependencies between BoundedContexts are allowed. This is achieved with the `jqassistant-context-mapper-plugin` (see section <> above): + +[[cml]] +[source, cml] +.link:jqassistant/context-mapper/context-map.cml[] +.... +include::jqassistant/context-mapper/context-map.cml[] +.... + +This `context-map.cml` defines that Objects from the BoundedContext `catalog` may be implemented in the BoundedContext `order`. Without this, the `jmolecules-ddd:IllegalDependenciesBetweenBoundedContexts`-constraint would fail. +This cml can be expanded to be applicable for bigger projects: + +[source, cml] +---- +ContextMap Project { + type = SYSTEM_LANDSCAPE + state = TO_BE + + contains order + contains catalog + contains yourNewBoundedContext + contains yourSecondNewBoundedContext + + catalog -> order + yourNewBoundedContext <-> order + yourSecondNewBoundedContext <- yourNewBoundedContext +} + +BoundedContext order { } +BoundedContext catalog { } +BoundedContext yourNewBoundedContext { } +BoundedContext yourSecondNewBoundedContext { } +---- + +This additionally allows that Objects from the BoundedContext `yourNewBoundedContext` may be implemented in the BoundedContext `yourSecondNewBoundedContext`. And that Objects from the BoundedContexts `yourNewBoundedContext` and `order` may implement each other. + +NOTE: `jmolecules-ddd:Strict` also contains the concept `jmolecules-ddd:AllowedBoundedContextDependency`. It reports :DEFINES_DEPENDENCY relations between :BoundedContext nodes. + +[[ProjectSpecificRules]] +== Project Specific Rules + +The concepts defined by the `jmolecules-ddd:Default` group 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 are written in XML files. + +The following examples will be defined in the file `link:jqassistant/architecture.xml[]`: + +.link:.jqassistant/architecture.xml[] +.... +include::jqassistant/architecture.xml[tag=architectureGroup,indent=0] +.... + +This defines a group `architecture:Default` which must be activated in the `.jqassistant.yml` file (see section <> above). +This group contains project specific constraints and concepts matching the intended project structure. + +=== 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 AggregateRoots (i.e. classes annotated with +`@AggregateRoot`) are returned by repositories (i.e. classes annotated with `@Repository`) + +.Aggregate constraint definition as defined in link:jqassistant/architecture.xml[] +.... +include::jqassistant/architecture.xml[tag=aggregates] +.... + +NOTE: The `requiresConcept` node defines, which concepts need to be executed before this constraint may be applied. + +=== Visualization of Building Blocks + +The current structure of the application can, when using the DDD-annotations (i.e. `@BoundedContext`) +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 link:jqassistant/architecture.xml[] +.... +include::jqassistant/architecture.xml[tag=boundedcontexts] +.... + +The resulting component diagram will be located in `target/jqassistant/report/plantuml/` and looks as following: + +[[plantumlPicture]] +image::architecture_BoundedContextOverview.svg[] + + +The project can be verified by running the following command: + +---- +mvn clean verify +---- + +== Resources + +1. link:https://jqassistant.github.io/jqassistant/doc/2.0.0/manual/[jQAssistant user manual] +2. link:https://github.com/xmolecules/jmolecules/blob/main/readme.adoc[jmolecules-ddd] +3. link:https://github.com/jqassistant-plugin/jqassistant-jmolecules-plugin#concepts-for-domain-driven-design[jQAssistant jmolecules plugin] +4. https://maven.apache.org[Apache Maven] diff --git a/ddd-plugin/src/main/java/your/company/project/ShopApplication.java b/jmolecules-ddd/src/main/java/your/company/project/ShopApplication.java similarity index 100% rename from ddd-plugin/src/main/java/your/company/project/ShopApplication.java rename to jmolecules-ddd/src/main/java/your/company/project/ShopApplication.java diff --git a/ddd-plugin/src/main/java/your/company/project/catalog/Product.java b/jmolecules-ddd/src/main/java/your/company/project/catalog/Product.java similarity index 56% rename from ddd-plugin/src/main/java/your/company/project/catalog/Product.java rename to jmolecules-ddd/src/main/java/your/company/project/catalog/Product.java index 599650b..d23352d 100644 --- a/ddd-plugin/src/main/java/your/company/project/catalog/Product.java +++ b/jmolecules-ddd/src/main/java/your/company/project/catalog/Product.java @@ -1,16 +1,17 @@ package your.company.project.catalog; -import lombok.Getter; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import lombok.*; +import org.jmolecules.ddd.annotation.AggregateRoot; @Getter -@DDD.AggregateRoot +// tag::dddType[] +@AggregateRoot public class Product { +// end::dddType[] private Long id; private String name; private ProductPrice price; - } diff --git a/ddd-plugin/src/main/java/your/company/project/catalog/ProductPrice.java b/jmolecules-ddd/src/main/java/your/company/project/catalog/ProductPrice.java similarity index 66% rename from ddd-plugin/src/main/java/your/company/project/catalog/ProductPrice.java rename to jmolecules-ddd/src/main/java/your/company/project/catalog/ProductPrice.java index 809ff4f..e9aa6da 100644 --- a/ddd-plugin/src/main/java/your/company/project/catalog/ProductPrice.java +++ b/jmolecules-ddd/src/main/java/your/company/project/catalog/ProductPrice.java @@ -2,10 +2,10 @@ import lombok.Getter; import lombok.Value; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.ValueObject; @Value -@DDD.ValueObject +@ValueObject public class ProductPrice { private double price; diff --git a/ddd-plugin/src/main/java/your/company/project/catalog/ProductRepository.java b/jmolecules-ddd/src/main/java/your/company/project/catalog/ProductRepository.java similarity index 67% rename from ddd-plugin/src/main/java/your/company/project/catalog/ProductRepository.java rename to jmolecules-ddd/src/main/java/your/company/project/catalog/ProductRepository.java index 4da0f58..a741e21 100644 --- a/ddd-plugin/src/main/java/your/company/project/catalog/ProductRepository.java +++ b/jmolecules-ddd/src/main/java/your/company/project/catalog/ProductRepository.java @@ -1,8 +1,8 @@ package your.company.project.catalog; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.Repository; -@DDD.Repository +@Repository public interface ProductRepository { Product findProductById(long id); diff --git a/ddd-plugin/src/main/java/your/company/project/catalog/ProductService.java b/jmolecules-ddd/src/main/java/your/company/project/catalog/ProductService.java similarity index 50% rename from ddd-plugin/src/main/java/your/company/project/catalog/ProductService.java rename to jmolecules-ddd/src/main/java/your/company/project/catalog/ProductService.java index 920c72d..4dc9865 100644 --- a/ddd-plugin/src/main/java/your/company/project/catalog/ProductService.java +++ b/jmolecules-ddd/src/main/java/your/company/project/catalog/ProductService.java @@ -1,7 +1,7 @@ package your.company.project.catalog; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.Service; -@DDD.Service +@Service public class ProductService { } diff --git a/jmolecules-ddd/src/main/java/your/company/project/catalog/package-info.java b/jmolecules-ddd/src/main/java/your/company/project/catalog/package-info.java new file mode 100644 index 0000000..455f8b1 --- /dev/null +++ b/jmolecules-ddd/src/main/java/your/company/project/catalog/package-info.java @@ -0,0 +1,4 @@ +@BoundedContext(name = "catalog") +package your.company.project.catalog; + +import org.jmolecules.ddd.annotation.BoundedContext; \ No newline at end of file diff --git a/ddd-plugin/src/main/java/your/company/project/order/Order.java b/jmolecules-ddd/src/main/java/your/company/project/order/Order.java similarity index 80% rename from ddd-plugin/src/main/java/your/company/project/order/Order.java rename to jmolecules-ddd/src/main/java/your/company/project/order/Order.java index 24f058c..a3fece3 100644 --- a/ddd-plugin/src/main/java/your/company/project/order/Order.java +++ b/jmolecules-ddd/src/main/java/your/company/project/order/Order.java @@ -1,11 +1,11 @@ package your.company.project.order; import lombok.AllArgsConstructor; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.AggregateRoot; import java.util.List; -@DDD.AggregateRoot +@AggregateRoot @AllArgsConstructor public class Order { diff --git a/ddd-plugin/src/main/java/your/company/project/order/OrderItem.java b/jmolecules-ddd/src/main/java/your/company/project/order/OrderItem.java similarity index 79% rename from ddd-plugin/src/main/java/your/company/project/order/OrderItem.java rename to jmolecules-ddd/src/main/java/your/company/project/order/OrderItem.java index f371e59..f0f693e 100644 --- a/ddd-plugin/src/main/java/your/company/project/order/OrderItem.java +++ b/jmolecules-ddd/src/main/java/your/company/project/order/OrderItem.java @@ -2,12 +2,12 @@ import lombok.AllArgsConstructor; import lombok.Getter; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.Entity; import your.company.project.catalog.Product; @Getter @AllArgsConstructor -@DDD.Entity +@Entity public class OrderItem { private long productId; diff --git a/jmolecules-ddd/src/main/java/your/company/project/order/OrderItemPrice.java b/jmolecules-ddd/src/main/java/your/company/project/order/OrderItemPrice.java new file mode 100644 index 0000000..2447624 --- /dev/null +++ b/jmolecules-ddd/src/main/java/your/company/project/order/OrderItemPrice.java @@ -0,0 +1,22 @@ +package your.company.project.order; + +import lombok.Getter; +import org.jmolecules.ddd.annotation.ValueObject; + +@Getter +@ValueObject +public class OrderItemPrice { + + private final double originalPrice; + + private final double discountPercentage; + + private final double finalPrice; + + public OrderItemPrice(double originalPrice, double discountPercentage) { + this.originalPrice = originalPrice; + this.discountPercentage = discountPercentage; + this.finalPrice = originalPrice * (1 - discountPercentage); + } + +} diff --git a/jmolecules-ddd/src/main/java/your/company/project/order/OrderRepository.java b/jmolecules-ddd/src/main/java/your/company/project/order/OrderRepository.java new file mode 100644 index 0000000..f060018 --- /dev/null +++ b/jmolecules-ddd/src/main/java/your/company/project/order/OrderRepository.java @@ -0,0 +1,15 @@ +package your.company.project.order; + +import org.jmolecules.ddd.annotation.Repository; +import org.jmolecules.ddd.annotation.Service; + +// tag::dddType[] +@Repository +public interface OrderRepository { + // end::dddType[] + + Order findById(long id); + + Order save(Order order); + +} diff --git a/ddd-plugin/src/main/java/your/company/project/order/OrderService.java b/jmolecules-ddd/src/main/java/your/company/project/order/OrderService.java similarity index 91% rename from ddd-plugin/src/main/java/your/company/project/order/OrderService.java rename to jmolecules-ddd/src/main/java/your/company/project/order/OrderService.java index daed9e4..438d072 100644 --- a/ddd-plugin/src/main/java/your/company/project/order/OrderService.java +++ b/jmolecules-ddd/src/main/java/your/company/project/order/OrderService.java @@ -1,13 +1,13 @@ package your.company.project.order; -import org.jqassistant.contrib.plugin.ddd.annotation.DDD; +import org.jmolecules.ddd.annotation.Service; import your.company.project.catalog.Product; import java.util.List; import java.util.stream.Collectors; // tag::dddType[] -@DDD.Service +@Service public class OrderService { // end::dddType[] diff --git a/jmolecules-ddd/src/main/java/your/company/project/order/package-info.java b/jmolecules-ddd/src/main/java/your/company/project/order/package-info.java new file mode 100644 index 0000000..eda40df --- /dev/null +++ b/jmolecules-ddd/src/main/java/your/company/project/order/package-info.java @@ -0,0 +1,4 @@ +@BoundedContext(name = "order") +package your.company.project.order; + +import org.jmolecules.ddd.annotation.BoundedContext; \ No newline at end of file diff --git a/ddd-plugin/verify.bsh b/jmolecules-ddd/verify.bsh similarity index 100% rename from ddd-plugin/verify.bsh rename to jmolecules-ddd/verify.bsh