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

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

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

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

Spring Fest 2023での登壇資料

Avatar for YutoOtsuka

YutoOtsuka

March 17, 2023
Tweet

Other Decks in Programming

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ライフを︕