最新の安定バージョンについては、Spring Modulith 1.2.1 を使用してください!

基礎

Spring Modulith は、Spring Boot アプリケーションに論理モジュールを実装する開発者をサポートします。これにより、構造検証の適用、モジュールの配置のドキュメント化、個々のモジュールの統合テストの実行、実行時のモジュールの相互作用の観察、一般に疎結合方法でのモジュール相互作用の実装が可能になります。このセクションでは、開発者がテクニカルサポートに入る前に理解しておく必要がある基本的な概念について説明します。

アプリケーションモジュール

Spring Boot アプリケーションでは、アプリケーションモジュールは次の部分で構成される機能の単位です。

  • Spring Bean インスタンスによって実装される他のモジュールに公開される API、およびモジュールによって公開されるアプリケーションイベント。通常は、提供されたインターフェースと呼ばれます。

  • 他のモジュールからアクセスされることが想定されていない内部実装コンポーネント。

  • Spring Bean 依存関係、リッスンされるアプリケーションイベント、および公開される構成プロパティの形式で他のモジュールによって公開される API への参照。通常、必須インターフェースと呼ばれます。

Spring Moduliths は、Spring Boot アプリケーション内でモジュールを表現するさまざまな方法を提供します。主に、全体的な配置に含まれる複雑さのレベルが異なります。これにより、開発者は単純な方法から始めて、必要に応じてより高度な手段に自然に移行することができます。

シンプルなアプリケーションモジュール

アプリケーションのメインパッケージは、メインアプリケーションクラスが存在するパッケージです。つまり、@SpringBootApplication のアノテーションが付けられたクラスであり、通常はその実行に使用される main(…) メソッドが含まれています。デフォルトでは、メインパッケージの各直接サブパッケージは、アプリケーションモジュールパッケージとみなされます。

このパッケージにサブパッケージが含まれていない場合、単純なパッケージとみなされます。Java のパッケージスコープを使用して、他のパッケージに存在するコードから型が参照されないようにすることで、そのパッケージ内のコードを非表示にすることができ、その結果、他のパッケージへの依存性注入の影響を受けなくなります。当然のことながら、モジュールの API はパッケージ内のすべてのパブリック型で構成されます。

配置例を見てみましょう ( パブリック型を示します。 パッケージプライベートのもの)。

単一のインベントリアプリケーションモジュール
 Example
└─  src/main/java
   ├─  example                        (1)
   |  └─  Application.java
   └─  example.inventory              (2)
      ├─  InventoryManagement.java
      └─  SomethingInventoryInternal.java
1 アプリケーションのメインパッケージ example
2 アプリケーションモジュールパッケージ inventory

高度なアプリケーションモジュール

アプリケーションモジュールパッケージにサブパッケージが含まれている場合、まったく同じモジュールのコードから参照できるように、サブパッケージ内の型をパブリックにする必要がある場合があります。

在庫およびオーダーアプリケーションモジュール
 Example
└─  src/main/java
   ├─  example
   |  └─  Application.java
   ├─  example.inventory
   |  ├─  InventoryManagement.java
   |  └─  SomethingInventoryInternal.java
   ├─  example.order
   |  └─  OrderManagement.java
   └─  example.order.internal
      └─  SomethingOrderInternal.java

このような構成では、order パッケージは API パッケージとみなされます。他のアプリケーションモジュールのコードは、そのモジュール内の型を参照できます。order.internal は、アプリケーションモジュールの基本パッケージの他のサブパッケージと同様に、内部パッケージとみなされます。それら内のコードは他のモジュールから参照してはなりません。SomethingOrderInternal がパブリック型であることに注意してください。これは、おそらく OrderManagement がそれに依存しているためです。残念ながら、これは、inventory パッケージなどの他のパッケージからも参照できることを意味します。この場合、Java コンパイラーは、これらの不正な参照を防ぐためにあまり役に立ちません。

アプリケーションモジュールの明示的な依存関係

モジュールは、package-info.java 型の @ApplicationModule アノテーションを使用して、許可される依存関係の宣言を選択できます。

モジュールの依存関係を明示的に構成するインベントリ
  • Java

  • Kotlin

@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order"
)
package example.inventory;
@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order"
)
package example.inventory

この場合、在庫モジュール内のコードは、オーダーモジュール内のコードを参照することのみが許可されていました (そして、コードはそもそもどのモジュールにも割り当てられていませんでした)。アプリケーションモジュール構造の検証でそれを監視する方法について調べましょう。

ApplicationModules 型

Spring Moduliths を使用すると、コードベースをインスペクションして、指定された配置とオプションの構成に基づいてアプリケーションモジュールモデルを導出できます。spring-modulith-core アーティファクトには、Spring Boot アプリケーションクラスを指すことができる ApplicationModules が含まれています。

アプリケーションモジュールモデルの作成
  • Java

  • Kotlin

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

分析された配置がどのように見えるかを把握するには、モデル全体に含まれる個々のモジュールをコンソールに書き込むだけです。

アプリケーションモジュールの配置をコンソールに書き込む
  • Java

  • Kotlin

modules.forEach(System.out::println);
modules.forEach(println(it))
アプリケーションモジュール構成のコンソール出力
## example.inventory ##
> Logical name: inventory
> Base package: example.inventory
> Spring beans:
  + ….InventoryManagement
  o ….SomeInternalComponent

## example.order ##
> Logical name: order
> Base package: example.order
> Spring beans:
  + ….OrderManagement
  + ….internal.SomeInternalComponent

各モジュールがどのようにリストされ、含まれる Spring コンポーネントが識別され、それぞれの可視性もレンダリングされるかに注目してください。

名前付きインターフェース

デフォルトでは、および高度なアプリケーションモジュールに従って、アプリケーションモジュールの基本パッケージは API パッケージとみなされ、他のモジュールからの依存関係を受け入れることを許可する唯一のパッケージです。追加のパッケージを他のモジュールに公開したい場合は、名前付きインターフェースを使用する必要があります。これを実現するには、これらのパッケージの package-info.java ファイルに @NamedInterface のアノテーションを付けます。

SPI 名前付きインターフェースをカプセル化するパッケージの配置
 Example
└─  src/main/java
   ├─  example
   |  └─  Application.java
   ├─ …
   ├─  example.order
   |  └─  OrderManagement.java
   ├─  example.order.spi
   |  ├—  package-info.java
   |  └─  SomeSpiInterface.java
   └─  example.order.internal
      └─  SomethingOrderInternal.java
package-info.java の example.order.spi
  • Java

  • Kotlin

@org.springframework.modulith.NamedInterface("spi")
package example.order.spi;
@org.springframework.modulith.NamedInterface("spi")
package example.order.spi

この宣言の効果は 2 つあります。まず、他のアプリケーションモジュールのコードが SomeSpiInterface を参照できるようになります。アプリケーションモジュールは、明示的な依存関係宣言で名前付きインターフェースを参照できます。インベントリーモジュールがそれを利用していると仮定すると、上記で宣言された名前付きインターフェースを次のように参照できます。

  • Java

  • Kotlin

@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order::spi"
)
package example.inventory;
@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order::spi"
)
package example.inventory

名前付きインターフェースの名前 spi を二重コロン :: で連結する方法に注目してください。この設定では、インベントリ内のコードは、SomeSpiInterface および order.spi インターフェースに存在する他のコードに依存することが許可されますが、たとえば OrderManagement には依存しません。依存関係が明示的に記述されていないモジュールの場合、アプリケーションモジュールのルートパッケージと SPI パッケージの両方にアクセスできます。

モジュール検出のカスタマイズ

デフォルトのアプリケーションモジュールモデルがアプリケーションで機能しない場合は、ApplicationModuleDetectionStrategy の実装を提供することでモジュールの検出をカスタマイズできます。このインターフェースは単一のメソッド Stream<JavaPackage> getModuleBasePackages(JavaPackage) を公開し、Spring Boot アプリケーションクラスが存在するパッケージとともに呼び出されます。その後、その中に存在するパッケージをインスペクションし、命名規則などに基づいてアプリケーションモジュールの基本パッケージとみなされるパッケージを選択できます。

次のようにカスタム ApplicationModuleDetectionStrategy 実装を宣言するとします。

  • Java

  • Kotlin

package example;

class CustomApplicationModuleDetectionStrategy implements ApplicationModuleDetectionStrategy {

  @Override
  public Stream<JavaPackage> getModuleBasePackages(JavaPackage basePackage) {
    // Your module detection goes here
  }
}
package example

class CustomApplicationModuleDetectionStrategy : ApplicationModuleDetectionStrategy {

  override fun getModuleBasePackages(basePackage: JavaPackage): Stream<JavaPackage> {
    // Your module detection goes here
  }
}

このクラスは、次のように META-INF/spring.factories に登録する必要があります。

org.springframework.modulith.core.ApplicationModuleDetectionStrategy=\
  example.CustomApplicationModuleDetectionStrategy