diff --git a/Gemfile.lock b/Gemfile.lock index 48827de65..bb19bcbee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.4.3) + activesupport (7.0.7.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -39,7 +39,7 @@ GEM activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.8.0) - i18n (1.12.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) jekyll (4.3.2) addressable (~> 2.4) @@ -82,7 +82,7 @@ GEM rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.4.0) mini_portile2 (2.8.1) - minitest (5.18.0) + minitest (5.19.0) multipart-post (2.1.1) nokogiri (1.14.3) mini_portile2 (~> 2.8.0) diff --git a/_data/navigation.yml b/_data/navigation.yml index 7b3fa472a..06ec00f6a 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -10,6 +10,6 @@ main: - title: "User Guide" url: /userguide/html/000_Index.html - title: "API" - url: https://javadoc.io/doc/com.tngtech.archunit/archunit/1.1.0 + url: https://javadoc.io/doc/com.tngtech.archunit/archunit/1.2.0 - title: "About" url: /about diff --git a/_pages/getting-started.md b/_pages/getting-started.md index 2e4060e2e..e5121b462 100644 --- a/_pages/getting-started.md +++ b/_pages/getting-started.md @@ -15,7 +15,7 @@ ArchUnit can be obtained from Maven Central. com.tngtech.archunit archunit - 1.1.0 + 1.2.0 test ``` @@ -23,7 +23,7 @@ ArchUnit can be obtained from Maven Central. #### Gradle ```groovy dependencies { - testImplementation 'com.tngtech.archunit:archunit:1.1.0' + testImplementation 'com.tngtech.archunit:archunit:1.2.0' } ``` diff --git a/_posts/2023-11-06-release-v1.2.0.markdown b/_posts/2023-11-06-release-v1.2.0.markdown new file mode 100644 index 000000000..2fab33ea4 --- /dev/null +++ b/_posts/2023-11-06-release-v1.2.0.markdown @@ -0,0 +1,8 @@ +--- +layout: splash +title: "New release of ArchUnit (v1.2.0)" +date: 2023-11-06 12:00:00 +categories: news release +--- + +A new release of ArchUnit (v1.2.0) is out. For details see [the release on GitHub](https://github.com/TNG/ArchUnit/releases/tag/v1.2.0 "ArchUnit v1.2.0 on GitHub"). diff --git a/userguide/007_The_Lang_API.adoc b/userguide/007_The_Lang_API.adoc index 18bf1210a..e472d3c80 100644 --- a/userguide/007_The_Lang_API.adoc +++ b/userguide/007_The_Lang_API.adoc @@ -278,7 +278,7 @@ ClassesTransformer packages = new AbstractClassesTransformer doTransform(JavaClasses classes) { Set result = new HashSet<>(); - classes.getDefaultPackage().accept(alwaysTrue(), new PackageVisitor() { + classes.getDefaultPackage().traversePackageTree(alwaysTrue(), new PackageVisitor() { @Override public void visit(JavaPackage javaPackage) { result.add(javaPackage); diff --git a/userguide/008_The_Library_API.adoc b/userguide/008_The_Library_API.adoc index d32b90a74..1c706e6a6 100644 --- a/userguide/008_The_Library_API.adoc +++ b/userguide/008_The_Library_API.adoc @@ -115,10 +115,9 @@ note right on link #crimson: one adapter must not know\nabout any other adapter ---- - === Slices -Currently there are two "slice" rules offered by the Library API. These are basically rules +Currently, there are two "slice" rules offered by the Library API. These are basically rules that slice the code by packages, and contain assertions on those slices. The entrance point is: [source,java,options="nowrap"] @@ -197,6 +196,108 @@ cycles.maxNumberToDetect=50 cycles.maxNumberOfDependenciesPerEdge=5 ---- +==== The Cycle Detection Core API + +The underlying infrastructure for cycle detection that the `slices()` rule makes use of can also be accessed +without any rule syntax around it. This allows to use the pure cycle detection algorithm in custom +checks or libraries. The core class of the cycle detection is + +[source,java,options="nowrap"] +---- +com.tngtech.archunit.library.cycle_detection.CycleDetector +---- + +It can be used on a set of a generic type `NODE` in combination with a generic `Set` +(where `EDGE implements Edge`) representing the edges of the graph: + +[source,java,options="nowrap"] +---- +Set nodes = // ... +Set> edges = // ... +Cycles> foundCycles = CycleDetector.detectCycles(nodes, edges); +---- + +Edges are parameterized by a generic type `EDGE` to allow custom edge types that can +then transport additional meta-information if needed. + + +=== Modularization Rules + +[NOTE] +Note: ArchUnit doesn't strive to be a "competition" for module systems like the +Java Platform Module System. Such systems have advantages like checks at compile time +versus test time as ArchUnit does. So, if another module system works well in your +environment, there is no need to switch over. But ArchUnit can bring JPMS-like features +to older code bases, e.g. Java 8 projects, or environments where the JPMS is for some +reason no option. It also can accompany a module system by adding additional rules e.g. +on the API of a module. + +To express the concept of modularization ArchUnit offers `ArchModule`s. The entrypoint into +the API is `ModuleRuleDefinition`, e.g. + +[source,java,options="nowrap"] +---- +ModuleRuleDefinition.modules().definedByPackages("..example.(*)..").should().beFreeOfCycles(); +---- + +As the example shows, it shares some concepts with the <> API. For example `definedByPackages(..)` +follows the same semantics as `slices().matching(..)`. +Also, the configuration options for cycle detection mentioned in the last section are shared by these APIs. +But, it also offers several powerful concepts beyond that API to express many different modularization scenarios. + +One example would be to express modules via annotation. We can introduce a custom annotation +like `@AppModule` and follow a convention to annotate the top-level `package-info` file +of each package we consider the root of a module. E.g. + +[source,java,options="nowrap"] +.com/myapp/example/module_one/package-info.java +---- +@AppModule( + name = "Module One", + allowedDependencies = {"Module Two", "Module Three"}, + exposedPackages = {"..module_one.api.."} +) +package com.myapp.example.module_one; +---- + +We can then define a rule using this annotation: + +[source,java,options="nowrap"] +---- +modules() + .definedByAnnotation(AppModule.class) + .should().respectTheirAllowedDependenciesDeclaredIn("allowedDependencies", + consideringOnlyDependenciesInAnyPackage("..example..")) + .andShould().onlyDependOnEachOtherThroughPackagesDeclaredIn("exposedPackages") +---- + +As the example shows, the syntax carries on meta-information (like the annotation of the annotated +`package-info`) into the created `ArchModule` objects where it can +be used to define the rule. In this example, the allowed dependencies are taken from the `@AppModule` +annotation on the respective `package-info` and compared to the actual module dependencies. Any +dependency not listed is reported as violation. +Likewise, the exposed packages are taken from the `@AppModule` annotation and any dependency +where the target class's package doesn't match any declared package identifier is reported +as violation. + +Note that the `modules()` API can be adjusted in many ways to model custom requirements. +For further details, please take a look at the examples provided +https://github.com/TNG/ArchUnit-Examples/blob/main/example-junit5/src/test/java/com/tngtech/archunit/exampletest/junit5/ModulesTest.java[here]. + +==== Modularization Core API + +The infrastructure to create modules and inspect their dependencies can also be used outside +the rule syntax, e.g. for custom checks or utility code: + +[source,java,options="nowrap"] +---- +ArchModules modules = ArchModules.defineByPackages("..example.(*)..").modularize(javaClasses); +ArchModule coreModule = modules.getByIdentifier("core"); +Set> coreDependencies = coreModule.getModuleDependenciesFromSelf(); +coreDependencies.forEach(...); +---- + + === General Coding Rules The Library API also offers a small set of coding rules that might be useful in various projects. diff --git a/userguide/html/000_Index.html b/userguide/html/000_Index.html index eb148fe57..0586bac15 100644 --- a/userguide/html/000_Index.html +++ b/userguide/html/000_Index.html @@ -449,7 +449,7 @@ @@ -647,7 +648,7 @@

2.2. JUnit 5

<dependency>
     <groupId>com.tngtech.archunit</groupId>
     <artifactId>archunit-junit5</artifactId>
-    <version>1.1.0</version>
+    <version>1.2.0</version>
     <scope>test</scope>
 </dependency>
@@ -656,7 +657,7 @@

2.2. JUnit 5

build.gradle
dependencies {
-    testImplementation 'com.tngtech.archunit:archunit-junit5:1.1.0'
+    testImplementation 'com.tngtech.archunit:archunit-junit5:1.2.0'
 }
@@ -673,7 +674,7 @@

<dependency> <groupId>com.tngtech.archunit</groupId> <artifactId>archunit</artifactId> - <version>1.1.0</version> + <version>1.2.0</version> <scope>test</scope> </dependency> @@ -682,7 +683,7 @@

build.gradle
dependencies {
-   testImplementation 'com.tngtech.archunit:archunit:1.1.0'
+   testImplementation 'com.tngtech.archunit:archunit:1.2.0'
 }
@@ -1606,7 +1607,7 @@

8

8.2. Slices

-

Currently there are two "slice" rules offered by the Library API. These are basically rules +

Currently, there are two "slice" rules offered by the Library API. These are basically rules that slice the code by packages, and contain assertions on those slices. The entrance point is:

@@ -1875,9 +1876,131 @@

8.2.1. Co

+
+

8.2.2. The Cycle Detection Core API

+
+

The underlying infrastructure for cycle detection that the slices() rule makes use of can also be accessed +without any rule syntax around it. This allows to use the pure cycle detection algorithm in custom +checks or libraries. The core class of the cycle detection is

+
+
+
+
com.tngtech.archunit.library.cycle_detection.CycleDetector
+
+
+
+

It can be used on a set of a generic type NODE in combination with a generic Set<EDGE> +(where EDGE implements Edge<NODE>) representing the edges of the graph:

+
+
+
+
Set<MyNode> nodes = // ...
+Set<Edge<MyNode>> edges = // ...
+Cycles<Edge<MyNode>> foundCycles = CycleDetector.detectCycles(nodes, edges);
+
+
+
+

Edges are parameterized by a generic type EDGE to allow custom edge types that can +then transport additional meta-information if needed.

+
+
+ +
+

8.3. Modularization Rules

+
+ + + + + +
+ + +Note: ArchUnit doesn’t strive to be a "competition" for module systems like the +Java Platform Module System. Such systems have advantages like checks at compile time +versus test time as ArchUnit does. So, if another module system works well in your +environment, there is no need to switch over. But ArchUnit can bring JPMS-like features +to older code bases, e.g. Java 8 projects, or environments where the JPMS is for some +reason no option. It also can accompany a module system by adding additional rules e.g. +on the API of a module. +
+
+
+

To express the concept of modularization ArchUnit offers ArchModules. The entrypoint into +the API is ModuleRuleDefinition, e.g.

+
+
+
+
ModuleRuleDefinition.modules().definedByPackages("..example.(*)..").should().beFreeOfCycles();
+
+
+
+

As the example shows, it shares some concepts with the Slices API. For example definedByPackages(..) +follows the same semantics as slices().matching(..). +Also, the configuration options for cycle detection mentioned in the last section are shared by these APIs. +But, it also offers several powerful concepts beyond that API to express many different modularization scenarios.

+
+
+

One example would be to express modules via annotation. We can introduce a custom annotation +like @AppModule and follow a convention to annotate the top-level package-info file +of each package we consider the root of a module. E.g.

+
+
+
com/myapp/example/module_one/package-info.java
+
+
@AppModule(
+  name = "Module One",
+  allowedDependencies = {"Module Two", "Module Three"},
+  exposedPackages = {"..module_one.api.."}
+)
+package com.myapp.example.module_one;
+
+
+
+

We can then define a rule using this annotation:

+
+
+
+
modules()
+  .definedByAnnotation(AppModule.class)
+  .should().respectTheirAllowedDependenciesDeclaredIn("allowedDependencies",
+      consideringOnlyDependenciesInAnyPackage("..example.."))
+  .andShould().onlyDependOnEachOtherThroughPackagesDeclaredIn("exposedPackages")
+
+
+
+

As the example shows, the syntax carries on meta-information (like the annotation of the annotated +package-info) into the created ArchModule objects where it can +be used to define the rule. In this example, the allowed dependencies are taken from the @AppModule +annotation on the respective package-info and compared to the actual module dependencies. Any +dependency not listed is reported as violation. +Likewise, the exposed packages are taken from the @AppModule annotation and any dependency +where the target class’s package doesn’t match any declared package identifier is reported +as violation.

+
+
+

Note that the modules() API can be adjusted in many ways to model custom requirements. +For further details, please take a look at the examples provided +here.

+
+
+

8.3.1. Modularization Core API

+
+

The infrastructure to create modules and inspect their dependencies can also be used outside +the rule syntax, e.g. for custom checks or utility code:

+
+
+
+
ArchModules<?> modules = ArchModules.defineByPackages("..example.(*)..").modularize(javaClasses);
+ArchModule<?> coreModule = modules.getByIdentifier("core");
+Set<? extends ModuleDependency<?>> coreDependencies = coreModule.getModuleDependenciesFromSelf();
+coreDependencies.forEach(...);
+
+
+
-

8.3. General Coding Rules

+

8.4. General Coding Rules

The Library API also offers a small set of coding rules that might be useful in various projects. Those can be found within

@@ -1888,7 +2011,7 @@

<

-

8.3.1. GeneralCodingRules

+

8.4.1. GeneralCodingRules

The class GeneralCodingRules contains a set of very general rules and conditions for coding. For example:

@@ -1914,7 +2037,7 @@

8

-

8.3.2. DependencyRules

+

8.4.2. DependencyRules

The class DependencyRules contains a set of rules and conditions for checking dependencies between classes. For example:

@@ -1928,7 +2051,7 @@

8.3.2.

-

8.3.3. ProxyRules

+

8.4.3. ProxyRules

The class ProxyRules contains a set of rules and conditions for checking the usage of proxy objects. For example:

@@ -1943,7 +2066,7 @@

8.3.3. ProxyRules

-

8.4. PlantUML Component Diagrams as rules

+

8.5. PlantUML Component Diagrams as rules

The Library API offers a feature that supports PlantUML diagrams. This feature is located in

@@ -1989,7 +2112,7 @@

-

8.4.1. Configurations

+

8.5.1. Configurations

There are different ways to deal with dependencies of imported classes not covered by the diagram at all. The behavior of the PlantUML API can be configured by supplying a respective @@ -2061,7 +2184,7 @@

8.4.1

-

8.5. Freezing Arch Rules

+

8.6. Freezing Arch Rules

When rules are introduced in grown projects, there are often hundreds or even thousands of violations, way too many to fix immediately. The only way to tackle such extensive violations is to establish an @@ -2073,7 +2196,7 @@

FreezingArchRule will automatically reduce the known stored violations to prevent any regression.

-

8.5.1. Usage

+

8.6.1. Usage

To freeze an arbitrary ArchRule just wrap it into a FreezingArchRule:

@@ -2090,7 +2213,7 @@

8.5.1. Usage

-

8.5.2. Configuration

+

8.6.2. Configuration

By default FreezingArchRule will use a simple ViolationStore based on plain text files. This is sufficient to add these files to any version control system to continuously track the progress. @@ -2141,7 +2264,7 @@

8.5.2. Conf

-

8.5.3. Extension

+

8.6.3. Extension

FreezingArchRule provides two extension points to adjust the behavior to custom needs. The first one is the ViolationStore, i.e. the store violations will be recorded to. The second one @@ -2215,7 +2338,7 @@

-

8.6. Software Architecture Metrics

+

8.7. Software Architecture Metrics

Similar to code quality metrics, like cyclomatic complexity or method length, software architecture metrics strive to measure the structure and design of software. @@ -2240,7 +2363,7 @@

-

8.6.1. Cumulative Dependency Metrics by John Lakos

+

8.7.1. Cumulative Dependency Metrics by John Lakos

These are software architecture metrics as defined by John Lakos in his book "Large-Scale C++ Software Design". The basic idea is to calculate the DependsOn @@ -2310,7 +2433,7 @@

H
-

8.6.2. Component Dependency Metrics by Robert C. Martin

+

8.7.2. Component Dependency Metrics by Robert C. Martin

These software architecture metrics were defined by Robert C. Martin in various sources, for example in his book "Clean architecture : a craftsman’s guide to software structure and design".

@@ -2391,7 +2514,7 @@
<
-

8.6.3. Visibility Metrics by Herbert Dowalil

+

8.7.3. Visibility Metrics by Herbert Dowalil

These software architecture metrics were defined by Herbert Dowalil in his book "Modulare Softwarearchitektur: Nachhaltiger Entwurf durch Microservices, Modulithen und SOA 2.0".