最新の安定バージョンについては、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
のアノテーションを付けます。
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