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

Learning Domain-Driven Design輪読会 #17

issei
December 01, 2022

Learning Domain-Driven Design輪読会 #17

issei

December 01, 2022
Tweet

More Decks by issei

Other Decks in Programming

Transcript

  1. Chapter 15. Event-Driven Architecture (Introduction) • マイクロサービスのように、モダンな分散システムではevent-driven architecture(EDA)が一般的 ◦ 疎結合で、スケールしやすく、耐障害性を持つ分散システムを設計する時に

    は、イベント駆動なコミュニケーションを使えというアドバイスが多い • イベントドリブンアーキテクチャはDDDと繋げられやすい ◦ EDAはもちろんイベントベースで、DDDにもドメインイベントがある ◦ DDDのイベントをイベントドリブンアーキテクチャで使うのがコスパ良いように 思えるが...それって良いアイデアだろうか?
  2. Chapter 15. Event-Driven Architecture (Introduction) • イベントは、レガシーなシステムをたちまち疎結合な分散システムに変化させる ような「秘伝のたれ」ではない ◦ 完全に逆で、雑なEDAアプリケーションはモジュラモノリスを分散された大

    きな泥団子に変える • この章では、EDAとDDDの相互作用を見ていき、下記が分かる ◦ EDAの基礎 ◦ EDAの陥りがちな間違い ◦ DDDを活用しながら効果的な非同期統合システムを設計する方法
  3. Overview • 15.1 Event-Driven Architecture • 15.2 Events • 15.3

    Designing Event-Driven Integration • 15.4 Conclusion • 15.5 Exercises
  4. 16.1 Event-Driven Architecture • イベントドリブンアーキテクチャって何? ◦ イベントメッセージを交換することで、非同期 で相互にコミュニケーションする形式のアー キテクチャ(右図) •

    サービスのエンドポイントを同期的に呼び出す代わりに、コンポーネントはシステムのドメインに変 化があったことを他のコンポーネントに伝えるためにイベントをpublishする • コンポーネントはシステムで起きたイベントをsubscribeし、イベントに対し反応をすることができる • 第9章で説明したが、イベントドリブンの実行フローの典型例はsagaパターン
  5. 16.1 Event-Driven Architecture • イベントドリブンアーキテクチャとイベントソーシングの違いを明確にしておくことは重要 ◦ 第7章で述べたように、イベントソーシングは状態の遷移を一連のイベントとして捉えるため の方法 • イベントドリブンアーキテクチャとイベントソーシングはコンセプトが異なる

    ◦ EDAはサービス間のコミュニケーション ◦ イベントソーシングはサービス内での出来事 • イベントソーシングにおけるイベントはサービス内で実装された状態遷移(イベントソースドドメイン モデルでは集約の状態遷移)を表す ◦ それらはビジネスドメインの複雑さを捉えることが目的で、サービスを他のシステムコンポー ネントと統合することは意図していない
  6. 16.2 Events Events, Commands, and Messages • イベントの定義はメッセージパターンの定義と似ているが、異なるもの • イベントはメッセージだが、メッセージは必ずしもイベントである必要はない

    • メッセージには2種類ある ◦ イベント ▪ もうすでに起こった変化を記述するメッセージ ◦ コマンド ▪ 実行させなければならないオペレーションを記述するメッセージ • イベントとコマンドはメッセージとして非同期的にコミュニケーションさせることができる • コマンドは実行をキャンセルできる ◦ コマンドが無効、システムのビジネスルールに矛盾していたりするとき • 一方で、イベントはキャンセルできない ◦ すでに起こったことだから • キャンセルするには、代償となる行動 (sagaパターンでいう命令)を行うしかない • イベントは過去形にする
  7. 16.2 Events Structure • イベントは、シリアライズされ、メッセージプラット フォームを用いて送信できるデータレコード • 典型的なイベントのスキーマはイベントのメタ データとペイロード(イベントが伝えたい情報 )を

    含む • ペイロードはイベントが伝えたい情報だけでなく イベントのタイプも記述できる • 3つのイベントタイプについてこれから述べ、それ ぞれの違いを見ていく
  8. 16.2 Events Type of Events • イベント通知、イベントが運ぶ状態遷移 (ECST)、ドメインイベントの3つに大別される イベント通知 •

    イベント通知というのは、他のコンポーネントが反応するであろう、ビジネスドメインの変化にまつわるメッ セージ ◦ PayCheckGenerated, CampaignPublishedなど • イベント通知は簡潔であるのが良い ◦ 目的はイベントにおける利害関係者に通知することだが、それらが反応するための全ての情報を 伝えるべきではない
  9. 16.2 Events Type of Events/イベント通知 • 右のコードは、生成された給与明細を外部コンポーネントに通 知するイベント • 給与明細に関連するすべての情報を運ぶわけでなく、代わり

    に、受信者はリンクを使ってより詳細な情報を取得する • 通知のフローは右下図 • ある意味では、アメリカの WEA(Wireless Emergency Alert)シ ステムや欧州のEU-Alertに似ている ◦ 360文字制限のメッセージで非常事態を知らせる、という もの ◦ 非常事態の詳細は別の情報源で知る必要がある
  10. 16.2 Events Type of Events/イベント通知 • 簡潔なイベント通知は複数シナリオで好まれる ◦ セキュリティと並行性の二つについて見ていく セキュリティ

    • 受信側に詳細情報を問い合わせさせることで、メッセージングのインフラ上で機密情報の共有を 防ぎ、データにアクセスするために受信側は追加で認証が要るようにできる 並行性 • 非同期だとサブスクライバーに情報が届いた時には既に古い可能性がある ◦ 明示的に問い合わせることで最新の状態を取得することができる • さらに、並列なコンシューマーのうち一人だけがイベントを処理すればよいような場合、詳細の問 い合わせを悲観的ロックにできる ◦ これによりプロデューサー側は、他のコンシューマーが処理できないことを保証できる
  11. 16.2 Events Type of Events/ Event-carried state transfer(ECST) • ECSTはプロデューサー内部の状態変更を伝えるメッセージ

    • イベント通知メッセージと異なって、 ECSTメッセージは状態変化の全情報を含む • 二つ形式がある • 一つ目はエンティティの状態遷移の完全なスナップショット • 下のコードはcustomerの状態更新のスナップショット • データが大きくなる時には、修正があった部分だけ ECSTメッセージに含むのが良い
  12. 16.2 Events Type of Events/ Event-carried state transfer(ECST) • ECSTメッセージに含まれるのが完全なスナップショットでも更新フィールドだけでもイベントストリームは

    そのエンティティの状態のローカルキャッシュをコンシューマーに保持させ、それにしたがって動作させる ことができる • コンセプト的にはECSTメッセージは非同期的なデータの複製メカニズム • このアプローチでシステムはより耐障害性を持つ ◦ プロデューサーに障害があってもコンシューマーは動作できる • 複数のソースからのデータを処理する必要がある コンポーネントのパフォーマンス向上にも良い ◦ データが必要になるたびにデータソースに 問い合わせする代わりに、すべてのデータ をローカルにキャッシュすることができる
  13. 16.2 Events Type of Events/ Domain event • 3つめのイベントメッセージは 6章で述べたドメインイベント

    • ドメインイベントはイベント通知と ECSTメッセージの中間 ◦ どちらもビジネスドメインにおける重要なイベントを記述しており、そのイベントを記述するすべての データを含む ◦ 似ているけど、概念的に異なっている
  14. 16.2 Events Type of Events/ Domain event • どちらもプロデューサーのビジネスドメインの変化を表すが、2つのコンセプト的な違いがある •

    一つ目は、ドメインイベントはそのイベントを表す情報全てを含む ◦ コンシューマーは詳細を問い合わせる必要はない • 二つ目、モデリングの意図が異なる ◦ イベント通知は、他コンポーネントと統合を緩和させる意図がある ◦ ドメインイベントは、ビジネスドメインのモデル化、表現を意図している • ドメインイベントはたとえ外部コンシューマーが興味ないものであってもユースフル ◦ ドメインイベントを用いて可能な限りの状態遷移をモデル化するイベントソースシステムにおいて特 に顕著 • 外部コンシューマーが利用可能なすべてのドメインイベントに興味を持つことは、最適でない設計になる ◦ 本章の後半で詳しく説明する ドメインイベントとイベント通知
  15. 16.2 Events Type of Events/ Domain event • ドメインイベントに含まれるデータは、典型的な ECSTメッセージのスキーマとは概念的に異なる

    • ECSTメッセージは、プロデューサーのデータのローカルキャッシュを保持するのに十分な情報を提供す る • ドメインイベントは、そのようなリッチなモデルを公開しない • 特定のドメインイベントに含まれるデータでさえ、コンシューマーがサブスクライブしていない他のドメイン イベントが同じフィールドに影響を与えるかもしれないので、集約の状態をキャッシュするためには十分で はない • さらに、モデリングの意図が異なる • ドメインイベントに含まれるデータは、集約の状態を記述することを意図していない • その代わり、そのライフサイクルの間に起こったビジネスイベントを記述する ドメインイベントとECST
  16. 16.2 Events Type of Events/ Domain types: Example • 「結婚」をイベントとして

    3つのタイプを比較してみる • marriage-recordedはイベント通知メッセージ ◦ IDを持った人が結婚した事実以外の情報を持たない ◦ コンシューマーがさらに情報が欲しい場合は detailsフィールドの リンク • personal-details-changedはECSTメッセージ ◦ 人の個人情報の変化 (ラストネームの変化 )を表現する ◦ このメッセージでは変化の理由は説明しない ▪ 結婚したのか離婚したのか • marriedはドメインイベント ◦ ビジネスドメインにおけるイベントの性質にできるだけ近い形で モデル化されている ◦ その人のIDと、その人がパートナーの名前を引き受けたかどう かを示すフラグを含む
  17. 15.3 Designing Event-Driven Integration • 第3章で述べたようにソフトウェア設計というのはほとんど境界がテーマ • 何を内包する/しないか、最も重要なのは何が境界を超えるかを境界で定義する ◦ 境界を超える:コンポーネントの統合

    • EDAベースのシステムにおけるイベントは、コンポーネントの統合方法とコンポーネントの境界自体 の両方に影響を与える、第一級の設計要素 • イベントメッセージのタイプを正しく選択すれば、分散システムを構成(デカップリング)または破壊 (カップリング)することができる • この節では、いろんなイベントタイプを適応させる方法を学ぶ ◦ その前に、密結合の分散した大きな泥団子を設計するために、イベントをどのように使うか見 ていく
  18. 15.3 Designing Event-Driven Integration • 右図のシステムについて考える • CRM(境界付けられたコンテキスト)はイベントソースドメイン モデル •

    CRMはMarketing(境界付けられたコンテキスト)と統合した くなり、チームは イベントソースデータモデルの柔軟性を活 用し、Marketingに CRM のドメインイベントをサブスクライ ブさせ、ニーズに合ったモデルを投影させることにした Distributed Big Ball of Mud
  19. 15.3 Designing Event-Driven Integration • AdsOptimization (境界付けられたコンテキスト)が導入さ れ、CRM が生成する情報を処理する必要が生まれた •

    チームはまた、AdsOptimizationにCRMで生成されるドメイ ンイベントをサブスクライブさせ、AdsOptimizationのニーズ に合ったモデルを投影させることにした Distributed Big Ball of Mud
  20. 15.3 Designing Event-Driven Integration • 興味深いことに、Marketing と AdsOptimization は、同じ フォーマットで顧客の情報を表示する必要があった

    • その結果、CRM のドメインイベントから同じモデル、つま り、各顧客の状態のフラット化されたスナップショットを投影 することになった Distributed Big Ball of Mud
  21. 15.3 Designing Event-Driven Integration • Reporting(境界付けられたコンテキスト)は、CRM が発行 するドメインイベントの一部をサブスクライブし、 AdsOptimizationで実行される計算をフェッチするためのイ ベント通知メッセージとして使用した

    • しかし、同じイベントを使用して計算をトリガするため、 Reporting モデルが確実に更新されるように、 AdsOptimizationに遅延が発生した(Reportingの間違いか な?) ◦ メッセージを受信してから5分後に処理された • このよくないシステムの結合の種類を分析してみましょう Distributed Big Ball of Mud
  22. 15.3 Designing Event-Driven Integration • AdsOptimizationとReportingは時間的に結合されており、 実行順序に依存している ◦ AdsOptimizationが先でReportingが後 ◦

    この順序が逆だと、Reportingシステムで矛盾した データが生成される • 必要な実行順序を強制するために、エンジニアは Reporting システムに処理遅延を導入した • この 5 分間の遅延により、AdsOptimizationは必要な計算 を完了することができます。もちろん、これだけでは完璧で はない • AdsOptimizationが過負荷になり、5分を超えるかも • ネットワークの問題により、メッセージ配信が遅れるかも • AdsOptimizationが停止するかも Temporal Coupling
  23. 15.3 Designing Event-Driven Integration • MarketingとAdsOptimizationは、CRMのドメインイベントを サブスクライブし、顧客データの同じプロジェクションを実装 した • 受け取ったドメインイベントを状態ベースの表現に変換する

    ビジネスロジックは、両方の境界付けられたコンテキストで 重複し、変更の理由も同じ ◦ 同じフォーマットで顧客のデータを表示する必要が あった • 一方の境界付けられたコンテキストでプロジェクションが変 更されると、もう一方でも変更が要る • これは、機能的結合の一例 • 複数のコンポーネントが同じビジネス機能を実装しており、 それが変更されると、両方のコンポーネントが同時に変更さ れなければならない Functional Coupling
  24. 15.3 Designing Event-Driven Integration • このタイプの結合はより微妙 • MarketingとAdsOptimizationは、CRM のイベントソースモ デルによって生成されたすべてのドメイ

    ンイベントをサブス クライブする • したがって、新しいドメインイベントの追加や既存のイベント のスキーマの変更などのCRM の実装の変更は、両方に反 映されなければならない • これを怠ると、データに一貫性がなくなる ◦ 例えば、イベントのスキーマが変更されると、サブスク ライバーのプロジェクションロジックは失敗する ◦ ドメインイベントがCRMに追加された場合、プロジェク ションモデルに影響を与える可能性があるし、それを 無視すると矛盾した状態をプロジェクションすることに なる Implementation Coupling
  25. 15.3 Designing Event-Driven Integration • このように、やみくもにイベントを流し込んでも、システムはデ カップリングにも耐障害性にもならない • イベントを調整することで、設計を劇的に改善できることを見 てみましょう

    • CRMのデータモデルを構成するドメインイベントをすべて公開 すると、サブスクライバーとプロデューサーの実装の詳細が カップリングされる • 実装の結合は、より限定されたイベントのセットか、異なるタイ プのイベントを公開することで対処できる Refactoring the Event-Driven Integration
  26. 15.3 Designing Event-Driven Integration • MarketingとAdsOptimizationは、同じビジネス機能を実装す ることによって、互いに機能結合している • 実装と機能の結合の両方は、プロデューサーの投影ロジック をカプセル化することで対処できる

    • CRMは実装の詳細を公開せずに、コンシューマー主導になる • コンシューマーが必要とするモデルを投影し、それをコンテキ ストの公開言語にすることで、内部実装モデルから切り離され た統合固有のモデルとする • その結果、コンシューマはCRMの実装モデルを意識すること はない Refactoring the Event-Driven Integration
  27. 15.3 Designing Event-Driven Integration • AdsOptimization と Reporting の境界コンテキスト間の時間 的結合に対処するために、AdsOptimizationはイベント通知

    メッセージを発行して、Reportingに必要なデータをフェッチす るようトリガーをかけることができる Refactoring the Event-Driven Integration
  28. 15.3 Designing Event-Driven Integration • Andrew Groveが言うには、「パラノイアだけが生き残る」 • イベント駆動のシステムを設計するときの指針 ◦

    ネットワークは遅くなる ◦ サーバーは最も不都合な時に故障する ◦ イベントは順番にやってこない ◦ イベントは重複して発生する • 最も重要なのは、これらのイベントは週末や祝日に発生する可能性が高い • イベント駆動アーキテクチャの「駆動」は、システム全体がメッセージの正常な配信に依存しているこ とを意味する • したがって、「大丈夫だろう」という考え方は、絶対に避けなければならない Event-Driven Design Heuristics Assume the worst
  29. 15.3 Designing Event-Driven Integration • 何があっても、常に安定してイベントが配信されるようにする ◦ アウトボックスパターンを使って、確実にメッセージをパブリッシュする ◦ メッセージを発行する際には、サブスクライバーがメッセージの重複を排除し、順序の乱れた

    メッセージを識別して並べ替えられるようにする ◦ 補償アクションを発行する必要がある境界付けられたコンテキストをまたぐプロセスをオーケス トレーションするときは、saga とプロセスマネージャパターンを活用する Event-Driven Design Heuristics Assume the worst
  30. 15.3 Designing Event-Driven Integration Event-Driven Design Heuristics Use public and

    private events • 境界付けられたコンテキストの公開インターフェースを設計する際には、さまざまなタイプのイベント を活用する • ECST(Event-carried state transfer)メッセージは、実装モデルをコンパクトなモデルに圧縮し、コン シューマーが必要とする情報のみを伝達する • イベント通知メッセージは、パブリックインタフェースをさらに最小化できる • 最後に、他の境界付けられたコンテキストとの通信のために、ドメインイベントを控えめに使用する • 専用のパブリックドメインイベントのセットを設計することを検討する
  31. 15.3 Designing Event-Driven Integration • イベント駆動の通信を設計する場合、イベントタイプを選択するための追加の評価法として、境界付 けられたコンテキストの一貫性要件を評価する • もしコンポーネントが最終的に一貫性のあるデータに落ち着くような場合、ECSTメッセージを使用す る

    • コンシューマーがプロデューサーの状態の最新の書き込みを読む必要がある場合、イベント通知 メッセージを発行し、その後にプロデューサーの最新の状態を取得するためのクエリを発行する Event-Driven Design Heuristics Evaluate consistency requirements
  32. 15.4 Conclusion • 境界付けられたコンテキストのパブリックインターフェースとして、イベント駆動アーキテクチャを紹 介した • 境界づけられたコンテキスト間のコミュニケーションに使える 3つのイベントタイプを学んだ • イベント通知

    ◦ 重要な出来事が起こったことの通知。コンシューマーに更なる情報が必要な時、プロデュー サーに問い合わせる必要がある • Event-carried state transfer ◦ メッセージに基づいたデータ複製メカニズム。プロデューサーのデータのローカルキャッシュ の保持に使える状態のスナップショットを含む • ドメインイベント ◦ プロデューサーのビジネスドメインイベントを表現するメッセージ
  33. 15.5 Exercises 4. サービスS1, S2が非同期的に統合しています。 S2はS1にある最後に書き込みされたデータが必 要です。どのイベントのタイプがこの統合シナリオにフィットしますか? a. S1はevent-carried state

    transfer(ECST)を発行すべき b. S1はパブリックなイベント通知を発行すべき。 S1は最新情報を得るために同期的リクエスト をする c. S1はドメインイベントを発行すべき d. A and B
  34. 15.5 Exercises 4. サービスS1, S2が非同期的に統合しています。 S2はS1にある最後に書き込みされたデータが必 要です。どのイベントのタイプがこの統合シナリオにフィットしますか? a. S2はevent-carried state

    transfer(ECST)を発行すべき b. S2はパブリックなイベント通知を発行すべき。 S1は最新情報を得るために同期的リクエスト をする c. S2はドメインイベントを発行すべき d. A and B