メインページに戻る | Spring Framework & Spring Bootに戻る

Spring Modulith

Spring Modulithとは

Spring Modulithは、モノリシックアプリケーションをモジュール化するためのフレームワークです。マイクロサービスに分割することなく、単一のアプリケーション内で明確な境界を持つモジュールを作成し、保守性と拡張性を向上させます。Spring Modulithは、モジュール間の依存関係を明示的に定義し、アーキテクチャの整合性を確保するための機能を提供します。

Spring Modulithは、「モジュラーモノリス」アーキテクチャパターンを実装するためのSpringベースのソリューションです。このパターンは、マイクロサービスの複雑さを避けながら、モジュール性の利点を享受したいプロジェクトに最適です。Spring Modulithは、Spring Bootプロジェクトに簡単に統合でき、既存のSpringの機能(特にアプリケーションイベント)を活用して、モジュール間の疎結合な通信を実現します。

+------------------------------------------+
|           Spring Boot Application        |
|                                          |
|  +-------------+       +-------------+   |
|  |             |       |             |   |
|  |  Module A   |       |  Module B   |   |
|  |             |       |             |   |
|  +------+------+       +------+------+   |
|         |                     |          |
|         |     Event Bus       |          |
|         +---------------------+          |
|                   |                      |
|         +---------+---------+           |
|         |                   |           |
|  +------+------+    +------+------+    |
|  |             |    |             |    |
|  |  Module C   |    |  Module D   |    |
|  |             |    |             |    |
|  +-------------+    +-------------+    |
|                                         |
|        Spring Modulith Framework        |
+------------------------------------------+
        

図1: Spring Modulithアーキテクチャの概要 - モジュール間の通信はイベントバスを通じて行われる

Spring Modulithの主な目的は以下の通りです:

Spring Modulithの主な特徴

モジュール境界の詳細

Spring Modulithでは、Javaパッケージ構造を使用してモジュールの境界を定義します。各モジュールは、特定のパッケージとそのサブパッケージで構成されます。例えば、com.example.application.ordersパッケージは「orders」モジュールを表し、com.example.application.inventoryパッケージは「inventory」モジュールを表します。

モジュールの境界は、@Modulithアノテーションを使用して定義します。このアノテーションは、アプリケーションのルートパッケージに配置され、そのサブパッケージがモジュールとして扱われることを示します。例えば:

@org.springframework.modulith.Modulith
package com.example.application;

import org.springframework.modulith.Modulith;

また、特定のパッケージをモジュールとして明示的に定義するために、@org.springframework.modulith.Moduleアノテーションを使用することもできます:

@org.springframework.modulith.Module
package com.example.application.orders;

import org.springframework.modulith.Module;

依存関係の制御の詳細

Spring Modulithは、モジュール間の依存関係を制御するための強力な機能を提供します。デフォルトでは、モジュールは他のモジュールの内部実装に依存することはできません。モジュール間の依存関係は、明示的に公開されたAPIを通じてのみ許可されます。

公開APIは、@org.springframework.modulith.ApplicationModule.APIアノテーションを使用して定義します:

package com.example.application.orders.api;

import org.springframework.modulith.ApplicationModule;

@ApplicationModule.API
public interface OrderService {
    // 公開APIメソッド
}

また、特定のモジュールに対する依存関係を明示的に許可するために、@org.springframework.modulith.NamedInterfaceアノテーションを使用することもできます:

@org.springframework.modulith.NamedInterface("api")
package com.example.application.orders.api;

import org.springframework.modulith.NamedInterface;

イベント駆動アーキテクチャの詳細

Spring Modulithは、Spring ApplicationEventを使用して、モジュール間の疎結合な通信を実現します。イベントは、モジュールの境界を越えて情報を伝達するための主要な手段です。これにより、モジュール間の直接的な依存関係を減らし、システムの柔軟性と拡張性を向上させることができます。

イベントの発行は、ApplicationEventPublisherを使用して行います:

@Service
class OrderService {

    private final ApplicationEventPublisher events;

    OrderService(ApplicationEventPublisher events) {
        this.events = events;
    }

    @Transactional
    public Order createOrder(Order order) {
        // 注文の処理
        // ...
        
        // イベントの発行
        events.publishEvent(new OrderCreatedEvent(order));
        
        return order;
    }
}

イベントの購読は、@EventListenerアノテーションを使用して行います:

@Service
class InventoryService {

    @EventListener
    @Transactional
    void on(OrderCreatedEvent event) {
        // 在庫の更新
        // ...
    }
}

Spring Modulithの利点

モジュール性の向上

明確な境界を持つモジュールにより、コードの理解と保守が容易になります

アーキテクチャの整合性

モジュール間の依存関係を制御することで、アーキテクチャの整合性を確保します

段階的な進化

必要に応じて個々のモジュールをマイクロサービスに分割できます

開発の効率化

モノリスの単純さを維持しながら、モジュール化の利点を享受できます

Spring Modulithの主要コンポーネント

モジュール定義

パッケージ構造に基づいてアプリケーションのモジュールを定義します

依存関係チェック

モジュール間の許可された依存関係を強制するためのチェック機能

イベント発行

モジュール間の疎結合な通信のためのイベント駆動アーキテクチャ

ドキュメント生成

モジュール構造とその依存関係を視覚化するドキュメント生成機能

Spring Modulithの使用例

モジュール定義

@org.springframework.modulith.Modulith
package com.example.application;

import org.springframework.modulith.Modulith;

モジュール間のイベント発行

@Service
class OrderService {

    private final ApplicationEventPublisher events;

    OrderService(ApplicationEventPublisher events) {
        this.events = events;
    }

    @Transactional
    public Order createOrder(Order order) {
        // 注文の処理
        // ...
        
        // イベントの発行
        events.publishEvent(new OrderCreatedEvent(order));
        
        return order;
    }
}

イベントリスナー

@Service
class InventoryService {

    @EventListener
    @Transactional
    void on(OrderCreatedEvent event) {
        // 在庫の更新
        // ...
    }
}

Spring Modulithのテスト

Spring Modulithは、モジュール単位のテストを容易にするための強力な機能を提供します。@ModuleTestアノテーションを使用することで、特定のモジュールとその依存モジュールのみをロードしてテストを実行できます。これにより、テストの実行時間が短縮され、テストの信頼性が向上します。

基本的なモジュールテスト

@SpringBootTest
@ModuleTest
class OrderModuleTests {

    @Autowired OrderService orders;
    
    @Test
    void testCreateOrder() {
        // テストコード
        // ...
    }
}

イベント発行のテスト

Spring Modulithは、モジュール間のイベント発行をテストするための機能も提供します。ApplicationModuleTestクラスを使用することで、イベントの発行と購読をテストできます。

@SpringBootTest
@ModuleTest
class OrderModuleTests {

    @Autowired ApplicationModuleTest moduleTest;
    @Autowired OrderService orders;
    
    @Test
    void testOrderCreationPublishesEvent() {
        
        // イベント発行のテスト
        moduleTest.verify(orders)
            .publishesEventOfType(OrderCreatedEvent.class)
            .when(service -> service.createOrder(new Order()));
    }
}

モジュール間の依存関係のテスト

Spring Modulithは、モジュール間の依存関係を検証するためのテスト機能も提供します。これにより、アーキテクチャの整合性を確保できます。

@Test
void verifyModuleDependencies() {
    
    // モジュール間の依存関係の検証
    new Modulith(Application.class)
        .verify()
        .assertThatDependenciesAreValid();
}

Spring Modulithとマイクロサービスの比較

特徴 Spring Modulith マイクロサービス
デプロイ 単一のアプリケーションとしてデプロイ 個別のサービスとして独立してデプロイ
通信 メモリ内のイベント発行と購読 ネットワーク経由のAPI呼び出し
トランザクション 単一のトランザクション境界が可能 分散トランザクションが必要
スケーリング アプリケーション全体をスケーリング 個別のサービスを独立してスケーリング
複雑さ 比較的低い 比較的高い

Spring Modulithの導入ステップ

  1. Spring Bootプロジェクトに依存関係を追加
    <dependency>
        <groupId>org.springframework.modulith</groupId>
        <artifactId>spring-modulith-starter</artifactId>
        <version>1.1.0</version>
    </dependency>
  2. アプリケーションのルートパッケージに@Modulithアノテーションを追加
  3. パッケージ構造に基づいてモジュールを定義
  4. モジュール間の依存関係を明示的に定義
  5. イベント駆動アーキテクチャを使用してモジュール間の通信を実装
  6. モジュール単位のテストを作成

Spring Bootとの統合

Spring Modulithは、Spring Bootアプリケーションにシームレスに統合できます。Spring Bootの自動設定機能を活用することで、最小限の設定でSpring Modulithの機能を利用できます。

Spring Boot Starterの使用

Spring Modulithは、Spring Boot Starterを提供しており、必要な依存関係を自動的に追加します。

<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-starter</artifactId>
    <version>1.1.0</version>
</dependency>

また、特定の機能のみを使用する場合は、個別のスターターを追加することもできます。

<!-- イベント発行のみを使用する場合 -->
<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-events</artifactId>
    <version>1.1.0</version>
</dependency>

<!-- ドキュメント生成のみを使用する場合 -->
<dependency>
    <groupId>org.springframework.modulith</groupId>
    <artifactId>spring-modulith-docs</artifactId>
    <version>1.1.0</version>
</dependency>

Spring Bootの設定

Spring Modulithは、Spring Bootの設定ファイル(application.properties または application.yml)を使用して設定できます。

# application.properties
spring.modulith.events.enabled=true
spring.modulith.events.async.enabled=true
spring.modulith.events.persistence.enabled=true
# application.yml
spring:
  modulith:
    events:
      enabled: true
      async:
        enabled: true
      persistence:
        enabled: true

Spring Modulithのベストプラクティス

  1. モジュールの適切なサイズ設計: モジュールは大きすぎず小さすぎないサイズにします。一般的に、ドメイン駆動設計(DDD)の境界付けられたコンテキスト(Bounded Context)に対応するサイズが適切です。
  2. 明確なモジュール境界の定義: モジュールの境界を明確に定義し、パッケージ構造に反映させます。各モジュールは、特定のビジネス機能または技術的関心事に対応するべきです。
  3. 公開APIの最小化: モジュールの公開APIは最小限に保ち、必要な機能のみを公開します。内部実装の詳細は隠蔽し、モジュールの独立性を高めます。
  4. イベント駆動通信の活用: モジュール間の通信には、直接的な依存関係ではなく、イベント駆動アーキテクチャを活用します。これにより、モジュール間の疎結合性が高まります。
  5. 適切なイベント設計: イベントは、ドメインの変更を表現する意味のある単位で設計します。イベントには必要な情報のみを含め、過剰な情報は避けます。
  6. テストの自動化: モジュール単位のテストを自動化し、継続的インテグレーションパイプラインに組み込みます。これにより、モジュール間の依存関係の問題を早期に発見できます。
  7. ドキュメントの自動生成: Spring Modulithのドキュメント生成機能を活用して、モジュール構造とその依存関係を可視化します。これにより、アーキテクチャの理解と保守が容易になります。
  8. 段階的な導入: 既存のアプリケーションにSpring Modulithを導入する場合は、段階的に導入します。まず、アプリケーションの一部にモジュール境界を導入し、徐々に拡大していきます。
  9. マイクロサービスへの移行計画: 将来的にマイクロサービスへの移行を検討している場合は、モジュール境界をマイクロサービスの境界と一致させるように設計します。これにより、将来の移行が容易になります。

アンチパターン

まとめ

Spring Modulithは、モノリシックアプリケーションをモジュール化するための強力なフレームワークです。マイクロサービスに分割することなく、単一のアプリケーション内で明確な境界を持つモジュールを作成し、保守性と拡張性を向上させることができます。モジュール間の依存関係を明示的に定義し、イベント駆動アーキテクチャを使用することで、アーキテクチャの整合性を確保しながら、疎結合なモジュール間の通信を実現できます。

Spring Modulithの主な利点は以下の通りです:

Spring Modulithは、マイクロサービスアーキテクチャの複雑さを避けながら、モジュール化の利点を享受したいプロジェクトに最適なソリューションです。特に、将来的にマイクロサービスへの移行を検討しているが、現時点ではモノリシックアプリケーションの単純さを維持したいプロジェクトに適しています。