Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring Modulithで始めるモジュラモノリス開発

Spring Modulithで始めるモジュラモノリス開発

Spring Fest 2023での登壇資料

YutoOtsuka

March 17, 2023
Tweet

Transcript

  1. Copyright © Acroquest Technology Co., Ltd. All rights reserved. Spring

    Modulithで始める モジュラモノリス開発 Spring Fest 2023 Acroquest Technology株式会社 ⼤塚 優⽃ 1
  2. ⾃⼰紹介 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    2 l⼤塚優⽃ • Acroquest Technology株式会社 エンジニアリングクリエイター • Java(Spring), Python, TypeScript(Angular) • 2022年12⽉に会社の技術ブログ(Taste of Tech Topics)でSpring Modulithの記事を書きました
  3. ⽬次 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    3 1. Spring Modulithとは 2. モジュラモノリスとは 3. Spring Modulithの機能紹介 4. まとめ
  4. 1. Spring Modulithとは Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 4 モジュラモノリスなSpring Bootアプリの 開発をサポートしてくれるライブラリ ※実験的プロジェクトとして開発されており Spring Boot 3, Java17がベース
  5. 2. モジュラモノリスとは Copyright © Acroquest Technology Co., Ltd. All rights

    reserved. 6 l ソフトウェアアーキテクチャの⼀種である l モノリスアーキテクチャの仲間であり、ひとつのアプリケーションが モジュールという単位で分割された複数の機能・役割を持つ モノリス モジュラモノリス マイクロサービス ひとつのアプリが 全ての機能・役割を持つ ひとつのアプリが モジュールで明確に分割された 複数の機能・役割を持つ 複数のアプリ(サービス)が それぞれ1つの機能・役割を持つ デプロイ単位
  6. • モジュール化されているので構造が理解しやすい • モジュールごとの並列作業が⽐較的やりやすい • 将来的にマイクロサービスに移⾏しやすい モジュラモノリスのメリット・デメリット Copyright © Acroquest

    Technology Co., Ltd. All rights reserved. 7 • 単⼀のアプリなのでデプロイが容易 • ネットワーク越しの機能呼び出しが少ない • 全てのモジュールが常に利⽤可能 • E2Eテストが容易 • リファクタリングが容易 メリット モノリスとの差分
  7. モジュラモノリスのメリット・デメリット Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    8 • マイクロサービスほどの強い分離を強制できない • モジュール境界を管理する必要がある • 特定のモジュールをスケールさせることができない デメリット
  8. モノリスの課題点 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    10 Order Inventory Catalog 技術的観点による⽔平分割 UI層(@Controller) データアクセス層 (@Repository) ビジネスロジック層 (@Service) src/main/java controller service repository 密結合な実装になりやすい 複数⼈での並列作業がしづらい
  9. モジュール化によって疎結合にする Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    11 Order Inventory Catalog @Controller @Repository @Service @Controller @Repository @Service @Controller @Repository @Service ビジネス領域による垂直分割 src/main/java order inventory catalog モジュール同⼠が疎結合になりやすい 複数⼈での並列作業がしやすい
  10. モジュールのカプセル化によって疎結合にする Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    12 Order Inventory Catalog Public API 内部実装 内部実装 内部実装 発注 在庫 更新 l モジュールを跨ぐ呼び出しは パブリックなAPIにのみ依存する l パブリックAPIは⼩さく・変更の 少ない安定したものにする モジュール同⼠を疎結合に • 公開しているものを後から 隠すのは⼤変 • 依存している箇所に変更が 多いと修正が⼤変 l モジュールでは、パブリックなAPIと内部実装を明確に分ける Public API Public API
  11. Spring Modulithの依存関係追加 Copyright © Acroquest Technology Co., Ltd. All rights

    reserved. 14 <dependencies> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-modulith-starter-jpa</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-modulith-starter-test</artifactId> <scope>compile</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-modulith-bom</artifactId> <version>0.3.0</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> ・ライブラリのセットで構成されている ・使いたい機能を個別に追加 ※機能⼀覧等の詳細はドキュメント参照 このスライドで⽤いた依存関係
  12. ①アーキテクチャ違反の検証 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    16 XXXパッケージ YYYパッケージ ZZZパッケージ ・循環参照はNG ・パッケージ間の依存関係は こうしたい(こうなっているべき) ・別モジュールの内部実装は 参照しない 開発者 開発者の設計 コードの構造 ここの乖離がないか 検証する
  13. ①アーキテクチャ違反の検証 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    17 Order Inventory 公開API 内部実装 公開API 内部実装 src/main/java order inventory internal Application.java InternalComponent.java exposedOrderApi.java exposedInventoryApi.java l テストコードで、モジュールの内部実装への参照違反を検知できる other-internal OtherInternalComponent.java 凡例 モジュール 公開API 内部実装 public private
  14. ①アーキテクチャ違反の検証 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    18 l テストコード例 • OrderモジュールがInventoryモジュールの内部実装に依存している場合 org.springframework.modulith.model.Violations: - Module 'order' depends on non-exposed type com.example.samplemodulith.inventory.internal.InternalInventoryComponent within module 'inventory'! ログ import org.junit.jupiter.api.Test; import org.springframework.modulith.model.ApplicationModules; public class ModularityTests { @Test void verifyModularity() { var modules = ApplicationModules.of(SampleModulithApplication.class); modules.forEach(System.out::println); modules.verify(); } } @SpringBootApplicationが ついているクラスを指定
  15. ②モジュールごとに独⽴してテスト可能 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    20 ここだけでテスト可能 Aモジュール Bモジュール Cモジュール チームA担当 チームB担当 チームC担当
  16. ②モジュールごとに独⽴してテスト可能 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    21 l テストしたいモジュールに関連するBeanのみ⽣成してくれる スライスアノテーションのようなものが⽤意されている Order Inventory UI ビジネス ロジック データ アクセス @WebMvcTest @DataXXXTest @ApplicationModuleTest package com.example.samplemodulith.order; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.modulith.test.ApplicationModuleTest; @ApplicationModuleTest() class OrderIntegrationTests { @MockBean private InventoryService inventoryService; @Test void 何かしらのテスト() { } }
  17. イベント駆動アーキテクチャの概要 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    23 同期的 モジュールA モジュールB モジュールA モジュールB メソッド呼び出し イベント イベント発⾏ イベント購読 アプリA アプリB HTTP呼び出し アプリA アプリB メッセージブローカー ⾮同期的(イベント駆動) 同⼀ アプリ内の やり取り 複数 アプリ間の やり取り Spring Modulithでは 基本的にこの⽅式
  18. @Service class OrderService { private final InventoryService inventoryService; public OrderService1(InventoryService

    inventoryService) { this.inventoryService = inventoryService; } @Transactional public void complete() { inventoryService.update(); } } モジュール間のやり取りにイベントを利⽤する Copyright © Acroquest Technology Co., Ltd. All rights reserved. 24 l 別モジュールの公開APIを直接呼び出すのではなく、 イベントの公開と購読を利⽤する(Spring Application Events) 他モジュールのBeanを知っておく必要がある テストでは依存Beanをモックする必要がある 新機能を作成したときに、Orderモジュールを 修正する可能性がある 別モジュールの公開APIを直接呼び出す
  19. import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService

    { private final ApplicationEventPublisher publisher; public OrderService(ApplicationEventPublisher publisher) { this.publisher = publisher; } @Transactional public void complete(OrderCompleted event) { publisher.publishEvent(event); } } モジュール間のやり取りにイベントを利⽤する Copyright © Acroquest Technology Co., Ltd. All rights reserved. 25 l 別モジュールの公開APIを直接呼び出すのではなく、 イベントの公開と購読を利⽤する(Spring Application Events) イベントを公開する 他モジュールのことを知る必要がない
  20. モジュール間のやり取りにイベントを利⽤する Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    26 l イベントを購読する側は、Listenerの引数で購読するイベント型を受け取る l @EventListener(元のTxと同期的) l @ApplicationModuleListener(元のTxと⾮同期的) 発注 在庫 更新 発注 在庫 更新 ⾮同期で処理できる 在庫更新でエラー発⽣時はリトライ等が必要 トランザクション範囲 イベントを起点に後続の処理を実施 同じトランザクションで⼀貫性を維持できる 不必要にトランザクション範囲を広げる可能性がある @Service public class InventoryService { ... @EventListener void onSameTx(OrderCompleted event) { // ここで在庫更新処理 } @ApplicationModuleListener void onOtherTx(OrderCompleted event) { // ここで在庫更新処理 } ... } 凡例 どのListenerでエラーが起きたか という情報が必要
  21. ③イベント発⾏ログの永続化 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    27 モジュールA イベント ①イベント発⾏ ③イベント購読 モジュールB ②イベント発⾏ログの永続化 処理失敗時に リトライ対象のログを参照
  22. ③イベント発⾏ログの永続化(Event Publication Registry) Copyright © Acroquest Technology Co., Ltd. All

    rights reserved. 28 l イベント発⾏にフックして、イベントを購読している各Listenerごとに イベント発⾏ログを永続化 l @ApplicationModuleListenerが正常に処理された場合は そのイベントを完了状態にする 発注 在庫 更新 No error イベント公開 @ApplicationModuleListener 更新 未完了のイベントは、 デフォルトでアプリ再起動時に再送される ※H2の場合(他には HSQLDB, MySQL, PostgreSQL) COMPLETION_DATEは nullの状態で永続化 Error nullのまま
  23. 4. まとめ Copyright © Acroquest Technology Co., Ltd. All rights

    reserved. 29 l モジュラモノリスでは、内部をモジュール化することによって モノリスの恩恵を受けつつ疎結合な設計を保てる l Spring Modulithでは、テストを書くことで、 モジュールの意図しない参照を検知し、 開発者の設計と実際のコードに乖離がないことを機械的に判定できる l モジュール間のやり取りにイベント駆動を⽤いる際、 イベント発⾏ログの永続化機能によって リトライすべきイベントを判別できる
  24. 30 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    ご清聴ありがとうございました よいSpringライフを︕