{"id":568,"date":"2024-11-03T08:09:49","date_gmt":"2024-11-03T08:09:49","guid":{"rendered":"https:\/\/iugori.ro\/?p=568"},"modified":"2024-11-03T08:09:49","modified_gmt":"2024-11-03T08:09:49","slug":"executable-documentation-with-archunit-and-plantuml","status":"publish","type":"post","link":"https:\/\/iugori.ro\/?p=568","title":{"rendered":"Executable Documentation with ArchUnit and PlantUML"},"content":{"rendered":"\n\n\n<p class=\"wp-block-paragraph\">In modern software development, effective communication between various roles\u2014such as developers, architects, and QA engineers\u2014is crucial to building robust and maintainable systems. This is where <em>Executable Documentation<\/em> comes into play. Rather than relying on static documents that often become outdated or disconnected from the code, executable documentation uses automated, up-to-date tools that ensure all members of a team are aligned on architectural principles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Executable Documentation? <\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Executable Documentation<\/em> is a concept that extends beyond traditional documentation. It integrates architectural guidelines, design decisions, and domain rules directly into the development and testing process. By doing so, it creates a living set of guidelines that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Stay in sync with the codebase:<\/strong> Automated tools ensure that documentation reflects the current state of the system.<\/li>\n\n\n\n<li><strong>Facilitate collaboration:<\/strong> Different roles in a software project\u2014such as developers, architects, and testers\u2014can work together more effectively, as the documentation is actionable and always up-to-date.<\/li>\n\n\n\n<li><strong>Catch deviations early:<\/strong> Architectural and design constraints are enforced automatically, preventing costly issues later.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Tools and Practices for Executable Documentation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Executable documentation can encompass a wide range of tools and practices that serve to improve collaboration and enforce consistency. Here are some key examples:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Automated Architectural Tests (like ArchUnit):<\/strong> These tests ensure that architectural constraints and dependencies are respected across the codebase, catching violations early in the development process.<\/li>\n\n\n\n<li><strong>Visual Modeling Tools (like PlantUML):<\/strong> PlantUML diagrams offer a straightforward way to create and share visual models of the system&#8217;s architecture. These diagrams act as a single source of truth, helping the team to understand and adhere to architectural designs.<\/li>\n\n\n\n<li><strong>Code Generation Tools: <\/strong>Developed by experienced or senior developers, these tools can significantly enhance productivity and ensure code consistency. Here&#8217;s how:\n<ul class=\"wp-block-list\">\n<li><strong>Quick Onboarding:<\/strong> New developers can use these tools to generate boilerplate code that follows established patterns and conventions. This minimizes the learning curve and speeds up the development process.<\/li>\n\n\n\n<li><strong>Consistency Across the Codebase:<\/strong> Code generation tools help maintain a uniform structure and style, reducing the risk of deviation from architectural standards.<\/li>\n\n\n\n<li><strong>Reduced Coupling and Enhanced Flexibility:<\/strong> Instead of building and embedding a monolithic framework into every microservice, it is often more effective to develop coding assistants that generate code on demand. These assistants ensure that development is efficient and consistent but remain separate from the actual production code, preserving the flexibility of the system.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">A Better Alternative to Internal Frameworks<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In many organizations, internal frameworks are built to enforce architectural decisions and streamline development. However, these frameworks can become rigid and difficult to maintain, especially in a microservices architecture where flexibility is essential.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By opting for <em>code generation tools<\/em> instead:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Reduced Coupling:<\/strong> You avoid tightly coupling your codebase to an overarching framework. The generated code is independent, allowing each microservice to evolve independently while still following consistent patterns.<\/li>\n\n\n\n<li><strong>Separate from Production Code:<\/strong> Since these tools generate code rather than actively running in the system, they don\u2019t add complexity or runtime dependencies, making the system more maintainable.<\/li>\n\n\n\n<li><strong>Faster Development Cycles: <\/strong>Developers can focus on implementing business logic rather than boilerplate, reducing overall development time.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Implementing Executable Documentation with ArchUnit and PlantUML<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">ArchUnit<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/www.archunit.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">ArchUnit <\/a>is a Java library that provides a way to test architectural rules within your code. With ArchUnit, you can write tests that enforce architectural guidelines, such as package dependencies, layer constraints, and naming conventions..<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">PlantUML<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/plantuml.com\/component-diagram\" target=\"_blank\" rel=\"noreferrer noopener\">PlantUML <\/a>is a tool that allows you to create UML diagrams from a simple textual description. By using PlantUML, you can visualize your architecture and use it as a foundation for architectural rules enforced by ArchUnit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Setting Up the Project<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Step 1: Add Dependencies<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">In your build.gradle (or pom.xml), add dependencies for ArchUnit:<\/p>\n\n\n<pre><pre class=\"brush: groovy; title: ; notranslate\" title=\"\">\ndependencies {\n    testImplementation &#039;com.tngtech.archunit:archunit-junit5:1.3.0&#039;\n}\n<\/pre><\/pre>\n\n\n<h4 class=\"wp-block-heading\">Step 2: Define Your Component Diagram<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Create a PlantUML diagram to represent the architectural model you want to enforce and save it as <code>overview.puml<\/code>.<\/p>\n\n\n<pre><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@startuml\ntop to bottom direction\n\n&#x5B;EntryPoint] &lt;&lt;.._start&gt;&gt; as main\n&#x5B;Repository Internals] &lt;&lt;..repository.config..&gt;&gt; &lt;&lt;..repository.impl..&gt;&gt; as repo_internals\n&#x5B;Repository API] &lt;&lt;..repository.api..&gt;&gt; as repo_api\n&#x5B;Service] &lt;&lt;..service..&gt;&gt; as svc\n&#x5B;REST API] &lt;&lt;..web.rest..&gt;&gt; as rest\n\nmain --&gt; repo_api\nmain --&gt; repo_internals\nmain --&gt; svc\nmain --&gt; rest\nrepo_internals -&gt; repo_api\nsvc --&gt; repo_api\nrest --&gt; svc\n@enduml\n<\/pre><\/pre>\n\n\n<p class=\"wp-block-paragraph\">You can visualize the rendering of the above code below. The most important elements are the component stereotypes, as they represent the package specifiers that ArchUnit uses when checking dependencies. Specifically, the diagram enforces that the REST API layer (represented by packages containing <code>.web.rest.<\/code>) depends on the service layer (<code>.service.<\/code>), which in turn depends on the repository API layer (<code>.repository.api.<\/code>). Any dependency that violates this order will be flagged by the unit test as a breach of the architectural constraints.<\/p>\n\n\n<pre style=\"text-align: center;\"><p><img src=http:\/\/www.plantuml.com\/plantuml\/img\/PSz12eCm40NGVKun5p0d4A4B5-waTYT5eatBW6P2CbYyVMD5Ozhb_FdpI0IJ1IReYPacUA3J0oCPqPQ6tL8J6hv3dalPULQEYmAKXqbXQ5or9OzCRe7Ai7P6ZNxhkcCZ1p9FV4aP3dysEDdncmTmZJlSaOI-rDKVgYmcbs4MRQFTZ8FUoduRzfQVXu2MpIroRzq7qdE4F8lrOv1bHMJJ93xno5VwL0f8seIp5ieflnuSp0S0 alt=\"PlantUML Syntax:\ntop to bottom direction\n[EntryPoint] &lt;&lt;.._start&gt;&gt; as main\n[Repository Internals] &lt;&lt;..repository.config..&gt;&gt; &lt;&lt;..repository.impl..&gt;&gt; as repo_internals\n[Repository API] &lt;&lt;..repository.api..&gt;&gt; as repo_api\n[Service] &lt;&lt;..service..&gt;&gt; as svc\n[REST API] &lt;&lt;..web.rest..&gt;&gt; as rest\nmain --&gt; repo_api\nmain --&gt; repo_internals\nmain --&gt; svc\nmain --&gt; rest\nrepo_internals -&gt; repo_api\nsvc --&gt; repo_api\nrest --&gt; svc\n\" usemap=\"#plantuml_map\"><\/p><\/pre>\n\n\n<h4 class=\"wp-block-heading\">Step 3: Use Your PlantUML Model into and ArchUnit Test<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">You can use Java to parse the PlantUML model and convert it into a format that ArchUnit can understand. Like this:<\/p>\n\n\n<pre><pre class=\"brush: java; title: ; notranslate\" title=\"\">\npackage ro.iugori.arch;\n\nimport com.tngtech.archunit.core.importer.ImportOption;\nimport com.tngtech.archunit.junit.AnalyzeClasses;\nimport com.tngtech.archunit.junit.ArchTest;\nimport com.tngtech.archunit.lang.ArchRule;\nimport org.springframework.core.io.ResourceLoader;\n\nimport java.net.URL;\n\nimport static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;\nimport static com.tngtech.archunit.library.plantuml.rules.PlantUmlArchCondition.Configuration.*;\nimport static com.tngtech.archunit.library.plantuml.rules.PlantUmlArchCondition.adhereToPlantUmlDiagram;\n\n@AnalyzeClasses(packages = &quot;ro.iugori.yadvs&quot;, importOptions = {ImportOption.DoNotIncludeTests.class})\npublic class UMLDiagramTest {\n\n    private static final URL REFERENCE_DIAGRAM = ResourceLoader.class.getResource(&quot;\/arch\/overview.puml&quot;);\n\n    @ArchTest\n    static ArchRule UML_COMPLIANCE = classes()\n            .should(adhereToPlantUmlDiagram(REFERENCE_DIAGRAM, consideringOnlyDependenciesInDiagram()));\n\n}\n<\/pre><\/pre>\n\n\n<h4 class=\"wp-block-heading\">Step 4: Run Your Tests<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Run these tests using your preferred test runner (e.g., JUnit). If the tests pass, your architecture is compliant. If they fail, you&#8217;ll know exactly where the violations are.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The complete example is available on <a href=\"https:\/\/github.com\/iugori\/yadvs-start\/blob\/main\/be\/src\/test\/java\/ro\/iugori\/arch\/UMLDiagramTest.java\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Benefits of Using ArchUnit and PlantUML Together<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Clear visualization:<\/strong> PlantUML diagrams give you a visual representation of your architecture.<\/li>\n\n\n\n<li><strong>Automatic enforcement:<\/strong> ArchUnit tests ensure that your architecture remains intact as your code evolves.<\/li>\n\n\n\n<li><strong>Living documentation:<\/strong> Your documentation stays up-to-date, as any deviation will be flagged by the tests.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Combining ArchUnit and PlantUML as part of an <em>Executable Documentation<\/em> strategy ensures your architectural rules are both enforceable and clearly communicated. ArchUnit provides real-time enforcement of architectural constraints, while PlantUML keeps your architecture visually accessible and up to date. Together, they create a powerful system that keeps your codebase consistent and aligned with your design principles, fostering collaboration and maintaining architectural integrity across your team.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In modern software development, effective communication between various roles\u2014such as developers, architects, and QA engineers\u2014is crucial to building robust and maintainable systems. This is where Executable Documentation comes into play. Rather than relying on static documents that often become outdated or disconnected from the code, executable documentation uses automated, up-to-date tools that ensure all members [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[],"class_list":["post-568","post","type-post","status-publish","format-standard","hentry","category-techreport"],"_links":{"self":[{"href":"https:\/\/iugori.ro\/index.php?rest_route=\/wp\/v2\/posts\/568","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iugori.ro\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iugori.ro\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iugori.ro\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iugori.ro\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=568"}],"version-history":[{"count":37,"href":"https:\/\/iugori.ro\/index.php?rest_route=\/wp\/v2\/posts\/568\/revisions"}],"predecessor-version":[{"id":606,"href":"https:\/\/iugori.ro\/index.php?rest_route=\/wp\/v2\/posts\/568\/revisions\/606"}],"wp:attachment":[{"href":"https:\/\/iugori.ro\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=568"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iugori.ro\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=568"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iugori.ro\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=568"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}