Documenting Application Modules

通过`ApplicationModules`创建的应用程序模块模型可以用来创建文档摘录,以包含到用Asciidoc撰写的开发人员文档中。Spring Modulith的`Documenter`抽象可以生成两种不同类型的摘录:

  • 描述单个应用程序模块之间关系的 C4 和 UML 组件图

  • 所谓的 Application Module Canvas,关于模块和其中最相关元素的表格概览(Spring bean、聚合根、发布和侦听的事件以及配置属性)。

Generating Application Module Component diagrams

文档摘录可以通过将`ApplicationModules`实例传递到`Documenter`中来生成。

Generating application module component diagrams using Documenter
  • Java

  • Kotlin

class DocumentationTests {

  ApplicationModules modules = ApplicationModules.of(Application.class);

  @Test
  void writeDocumentationSnippets() {

    new Documenter(modules)
      .writeModulesAsPlantUml()
      .writeIndividualModulesAsPlantUml();
  }
}
class DocumentationTests {
    private val modules = ApplicationModules.of(Application::class)

    @Test
    fun writeDocumentationSnippets() {
        Documenter(modules)
            .writeModulesAsPlantUml()
            .writeIndividualModulesAsPlantUml()
    }
}

对`Documenter`的首次调用将生成一个C4组件图,其中包含系统中的所有模块。

All modules and their relationships rendered as C4 component diagram

top to bottom direction

Container_Boundary("Modulith.Application_boundary", "Application") { Component(Modulith.Application.core, "core", "Module", "", $tags="") Component(Modulith.Application.catalog, "catalog", "Module", "", $tags="") Component(Modulith.Application.inventory, "inventory", "Module", "", $tags="") Component(Modulith.Application.order, "order", "Module", "", $tags="") Component(Modulith.Application.customer, "customer", "Module", "", $tags="") }

Rel_D(Modulith.Application.order, Modulith.Application.core, "depends on", $tags="") Rel_D(Modulith.Application.order, Modulith.Application.customer, "uses", $tags="") Rel_D(Modulith.Application.catalog, Modulith.Application.core, "depends on", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.order, "listens to", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.catalog, "uses", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.order, "uses", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.core, "uses", $tags="") Rel_D(Modulith.Application.order, Modulith.Application.catalog, "depends on", $tags="")

SHOW_LEGEND()

第二次调用将创建其他图,这些图将仅包括模块及其直接依赖关系画布上。

A subset of application modules and their relationships starting from the order module rendered as C4 component diagram

top to bottom direction

Container_Boundary("Modulith.Application_boundary", "Application") { Component(Modulith.Application.core, "core", "Module", "", $tags="") Component(Modulith.Application.catalog, "catalog", "Module", "", $tags="") Component(Modulith.Application.order, "order", "Module", "", $tags="") Component(Modulith.Application.customer, "customer", "Module", "", $tags="") }

Rel_D(Modulith.Application.order, Modulith.Application.core, "depends on", $tags="") Rel_D(Modulith.Application.order, Modulith.Application.customer, "uses", $tags="") Rel_D(Modulith.Application.catalog, Modulith.Application.core, "depends on", $tags="") Rel_D(Modulith.Application.order, Modulith.Application.catalog, "depends on", $tags="")

SHOW_LEGEND()

Using Traditional UML Component Diagrams

如果您更喜欢传统的UML样式组件图,请调整`DiagramOptions`,以使用那种样式,如下所示:

  • Java

  • Kotlin

DiagramOptions.defaults()
  .withStyle(DiagramStyle.UML);
DiagramOptions.defaults()
  .withStyle(DiagramStyle.UML)

这将导致生成的图看起来像这样:

All modules and their relationships rendered as UML component diagram

skinparam { shadowing false arrowColor #707070 actorBorderColor #707070 componentBorderColor #707070 rectangleBorderColor #707070 noteBackgroundColor #ffffff noteBorderColor #707070 defaultTextAlignment center wrapWidth 200 maxMessageSize 100 componentStyle uml1 } package "Application" [Container] { component 4 [Component: Module] #dddddd [ com.acme.commerce.catalog ] component 3 [Component: Module] #dddddd [ com.acme.commerce.core ] component 7 [Component: Module] #dddddd [ com.acme.commerce.customer ] component 5 [Component: Module] #dddddd [ com.acme.commerce.inventory ] component 6 [Component: Module] #dddddd [ com.acme.commerce.order ] } 4 .[#707070].> 3 : depends on 5 .[#707070].> 4 : uses 5 .[#707070].> 3 : uses 5 .[#707070].> 6 : uses 5 .[#707070].> 6 : listens to 6 .[#707070].> 4 : depends on 6 .[#707070].> 3 : depends on 6 .[#707070].> 7 : uses

A subset of application modules and their relationships starting from the order module rendered as UML component diagram

skinparam { shadowing false arrowColor #707070 actorBorderColor #707070 componentBorderColor #707070 rectangleBorderColor #707070 noteBackgroundColor #ffffff noteBorderColor #707070 defaultTextAlignment center wrapWidth 200 maxMessageSize 100 componentStyle uml1 } package "Application" [Container] { component 4 [Component: Module] #dddddd [ com.acme.commerce.catalog ] component 3 [Component: Module] #dddddd [ com.acme.commerce.core ] component 7 [Component: Module] #dddddd [ com.acme.commerce.customer ] component 6 [Component: Module] #dddddd [ com.acme.commerce.order ] } 4 .[#707070].> 3 : depends on 6 .[#707070].> 4 : depends on 6 .[#707070].> 3 : depends on 6 .[#707070].> 7 : uses

Generating Application Module Canvases

可以通过调用`Documenter.writeModuleCanvases()`生成应用程序模块画布:

Generating application module canvases using Documenter
  • Java

  • Kotlin

class DocumentationTests {

  ApplicationModules modules = ApplicationModules.of(Application.class);

  @Test
  void writeDocumentationSnippets() {

    new Documenter(modules)
      .writeModuleCanvases();
  }
}
class DocumentationTests {

  private val modules = ApplicationModules.of(Application::class)

  @Test
  fun writeDocumentationSnippets() {
    Documenter(modules)
        .writeModuleCanvases()
  }
}

默认情况下,会在您的构建系统的构建文件夹中生成文档`spring-modulith-docs`文件夹。生成的画布看起来像这样:

Table 1. A sample Application Module Canvas

Base package

com.acme.commerce.inventory

Spring components

Services* c.a.c.i.InventoryManagement`Repositories* `c.a.c.i.Inventory`Event listeners* `c.a.c.i.InternalInventoryListeners 监听 o.s.m.m.DayHasPassedc.a.c.i.QuantityReduced* c.a.c.i.InventoryOrderEventListener 监听 c.a.c.o.OrderCanceledc.a.c.o.OrderCompleted`Configuration properties* `c.a.c.i.InventoryProperties`Others* `c.a.c.i.InventoryItemCreationListener

Aggregate roots

  • c.a.c.i.InventoryItem

Published events

  • c.a.c.i.QuantityReduced 已创建: c.a.c.i.InventoryItem.decreaseQuantity(…)* c.a.c.i.StockShort 已创建: c.a.c.i.InternalInventoryListeners.on(…)

Events listened to

  • c.a.c.o.OrderCompleted

  • c.a.c.o.OrderCanceled

Properties

  • acme.commerce.inventory.restock-threshold — c.a.c.c.QuantityInventoryEvents.StockShort 在库存更新期间被触发的阈值。

它包含以下部分:

  • The application module’s base package.

  • The Spring beans exposed by the application module, grouped by stereotype. - 换句话说,bean 位于 API 包或任何 named interface package 中。这将检测由 jMolecules architecture abstractions 定义的组件类型,但也将检测标准 Spring 类型注解。

  • Exposed aggregate roots - 任何我们为其找到存储库或通过 jMolecules 明确声明为聚合的实体。

  • Application events published by the module - 这些事件类型需要使用 jMolecules @DomainEvent 划定或实现其 DomainEvent 接口。

  • Application events listened to by the module - 源自使用 Spring @EventListener@TransactionalEventListener、jMolecules @DomainEventHandler 注释或实现 ApplicationListener 的 bean 的方法。

  • Configuration properties - 应用程序模块公开的 Spring Boot 配置属性。要求使用 spring-boot-configuration-processor 工件来提取附加到属性的元数据。