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

サブ資料③ドメインモデル中心の設計

Recruit
August 10, 2023

 サブ資料③ドメインモデル中心の設計

2023年度リクルート エンジニアコース新人研修の講義資料です

Recruit

August 10, 2023
Tweet

More Decks by Recruit

Other Decks in Technology

Transcript

  1. 1 ページ サブ資料③:ドメインモデル中⼼の設計(ドメイン駆動設計) ドメイン駆動設計について ドメインエキスパート ⾼度なアプリケーションになるほど、エンジニアの持つ業務知識だけではシステムに必要な 概念を理解したり、表現しきれなくなるため、ドメインエキスパートとの議論や擦り合わせ が必要になる。ドメインエキスパートは、担当業務やシステムの対象領域について最も理解 した⼈を指し、職種や役職を限定せず、CEOや役員、営業、エンジニア等、様々なケースがあ る他、⼀⼈ではなく複数⼈いる場合もある。なお、近年では専⾨職として採⽤するケースも

    増えている (次ページへ続く) 概要 ドメイン駆動設計(Domain-Driven Design, DDD)は、エリック・エヴァンズによって提唱され たソフトウェアの開発⼿法および設計⼿法。 特徴は、ビジネスドメインの複雑さを反映し、ソフトウェアをビジネス要件に対して柔軟か つ効果的に対応できるようにするために、ビジネスドメインの理解と表現に焦点を当て、ソ フトウェアの設計と実装を⾏う。 ドメイン中⼼の設計(=ドメインモデル中⼼) ドメイン駆動設計では、ソフトウェア開発の中⼼にビジネスドメインを置く。ドメインはソ フトウェアが扱う対象となるビジネスの知識や課題、ビジネスルール、ビジネスプロセス、 業務内容等を含んだ特定の領域(業界独⾃の⽤語や慣習なども含む)を指す。開発者はドメ インについて学び、ドメインエキスパートと協⼒し、ドメインを正確にモデル化することに 注⼒する。 ドメイン駆動設計の主な特徴と原則 モデル駆動設計 ドメイン駆動設計では、ドメインモデルを中⼼とした設計アプローチを採る。ドメインモデ ルは、ビジネスドメインを表現するための設計要素でビジネス要件を表現し、ソフトウェア の実装に反映される ユビキタス⾔語 ドメイン駆動設計では、ユビキタス⾔語と呼ばれる共通の⾔語を開発者とドメインエキス パート(※後述)の間で共有する。ユビキタス⾔語は、ビジネス要件を正確に表現し、コ ミュニケーションの障壁を減らす役割を果たす。なお、ユビキタス⾔語は、ドメインモデル やソースコードの中に直接組み込まれる。
  2. 2 ページ 例: ドメインモデルの種類 コンテキストの分割(境界づけられたコンテキスト) 世界 FictPayドメイン 送⾦ドメイ ン(サブ) 決済

    ドメイン (サブ) チャージ ドメイン (サブ) ビジネスドメイン毎にコンテキストを分割する エンティティ ドメイン駆動設計において、ビジネスドメインの中⼼的な存在で⼀意なものを表現する概念。 ⼀意であるため、⻑期にわたって変化するオブジェクトになる。例えば「社員」というエン ティティの場合、社員番号で社員を⼀意に識別することで、同じ⼈物であることを把握し、 住所や所属といった属性を変更できる。なお、⼀意に識別して変更を管理する必要がないも のは「値オブジェクト」にする (次ページへ続く) 境界づけられたコンテキスト 境界づけられたコンテキストは、ビジネス上の意味のある単位で、ドメインモデルを分割す るために使⽤されるコンテキスト(ビジネスドメインを包括する特定の範囲や境界を定めた 概念 ※後述)。 ドメインモデル中⼼の設計(ドメイン駆動設計)のメリット ①機能性を⾼められる これは、役に⽴つものを作ること、⾔い換えると「作ったけど使えない」を避けられやすい ②保守性を⾼めること ⻑期間開発しても機能拡張が容易でありつづけられる、つまり、「技術的負債でどんどん開 発速度が低下する」を避けられやすい。開発者も理解しやすい
  3. 3 ページ 集約 値オブジェクト ファクトリ リポジトリ その他ドメインモデル中⼼設計(ドメイン駆動設計)を構成する要素 集約は、複数の関連するエンティティ(Entity)や値オブジェクト(Value Object)をグループ 化し、⼀つのまとまった単位として扱う概念で、通常、ルートエンティティ(Aggregate

    Root)と呼ばれる特定のエンティティをルートとして、配下に他のエンティティや値オブ ジェクトを保持する形をとる。集約はドメインモデルの⼀貫性を保つための境界を定義し、 関連するエンティティや値オブジェクトの内部の状態をカプセル化する。 「値オブジェクト」は何かを計測したり、定量化したりして説明する際に使⽤するオブジェ クトで⼀意に識別が必要なエンティティに対して値オブジェクトは⼀意に識別する必要を持 たないオブジェクトを指す。例えば、「社員」エンティティは「社員ID」を識別⼦として⼀意 に識別されるが、「社員ID」そのものは⼀意な識別⼦を必要としないため、値オブジェクトに 分類される。そのため、「社員」エンティティのプロパティのうち、社員IDは数値型(int型 等)やString型を使うのではなく、EmployeeId型やPhoneNumber型を作ることで、ドメインの 業務をソースコードでわかりやすく⽰すことができる。また、値オブジェクトはエンティ ティとは異なり、内部で保持する値は変更不可(Immutable)であるのが原則(デザインパ ターンのValue Objectパターン)。なお、通常エンティティが値オブジェクトを保持すること が多いが、 逆に値オブジェクトがエンティティを保持することはない 「ファクトリ」は⼀般的には複雑な⽣成処理を分離することで、全体を理解しやすくする仕 組みであるが、ドメイン駆動設計においては、以上に加えて「ユビキタス⾔語を⽤いて集約 をシンプルに⽣成する」意味合いが強くなる。ユビキタス⾔語で業務を表現し、⾒せるべき でない情報を保護し、整合性の取れた集約を⽣成する リポジトリは、ドメインモデルとデータベース(または永続化層)の間の仲介役で、ドメイ ンモデルに対するデータアクセスの抽象化を提供し、ドメインモデルの永続化や取得、更新 などの操作を⾏う DAO DAO(Data Access Object)はデータアクセスのためのパターンとして考案されたデザイン パターンの⼀つで、データベースや永続化ストレージとの直接的なやり取りを担当し、 データのCRUD(作成、読み取り、更新、削除)などの操作を提供する。ドメイン駆動設 計を構成する正式な要素ではないものの使⽤されることもある (次ページへ続く)
  4. 4 ページ ドメインイベント サービス 「ドメインイベント」は、ドメインで起こった出来事をモデリングする仕組み。ドメインイ ベントを使うことで、複雑になりがちな出来事を管理し、システム間の連携を柔軟にするこ とができる。なお、ドメインイベントはエリック・エヴァンズがDDD提唱後に発展した概念 ドメインイベントの利点(バッチ処理の代替可能性) 通常負荷がかかる処理等は、夜間にバッチ処理で⾏うことが多いが、ドメインイベントは随 時通知されるため、その際、必要な対象のみに絞った処理を通知に付随しておこなうことが

    可能になる。このため、特定のケースにおいては、通常のバッチ処理を代替する⽅法となる 可能性がある Application Service(アプリケーションサービス) Domain Service(ドメインサービス) Application Serviceは、アプリケーションのビジネスロジックを処理するためのサービス クラスで、アプリケーションのユースケースを表現し、複数のドメインオブジェクト (モデル)やドメインサービスを組み合わせて処理を実⾏する。Application Serviceはト ランザクションの管理や外部インタフェースとのやり取りなど、アプリケーション全体 の制御フローを担当する ドメインサービスは、ドメイン駆動設計において、ドメインモデルが持つべきでない依 存関係や技術的な詳細をカプセル化し、ドメインモデルのシンプルさと独⽴性を保つ役 割を果たす。ドメインサービスはドメインオブジェクトとは独⽴しており、複数のドメ インオブジェクトを跨いで機能を提供する場合もある。なお、ドメインサービスはリポ ジトリを呼び出すことは可能 元はモデルから派⽣した概念。フレームワークや設計パラダイムによってはサービスを採⽤ していないケースもある 補⾜)Shared Service Shared Serviceはドメイン駆動設計で⽤いられる概念ではないものの、⼀部の設計パラダイ ムやフレームワークで⽤いられているServiceの概念で 複数のControllerやServiceクラスで、共有(再利⽤ ※つまり使い回される)されるロジックを 提供する。 なお、SharedServiceクラスからServiceクラスのメソッドを呼び出すことは⼀般には推奨さ れない リポジトリとDAOの違いと併⽤について リポジトリとDAOは似ているものの、考案されたパラダイムが異なるため、⽐較が難し いが、併⽤する場合は、RepositoryがDAOを利⽤してデータの永続化や取得を⾏う構造と なる。Repositoryはドメインモデルに対するインターフェースを提供し、ドメインモデル を操作する際にはRepositoryを介してデータアクセスを⾏い、内部的にはepositoryがDAO を使⽤してデータの保存や検索、変更などを⾏う
  5. 5 ページ 問題領域と解決領域 問題領域 問題領域は、ビジネス上のルール、プロセス、戦略課題等を含んでおり、ソフトウェアが解 決すべき実際の問題を分析・明確化する領域 解決領域 問題領域に対する具体的な解決策や技術的な実装を⾏う領域 コアドメイン 「コアドメイン」とは事業的に最も重要で戦略的に不可⽋なもの

    事業的に他と差別化されており、最優先で取り組まれる サブドメイン システム的に必要でありつつもコアドメインではないものを「サブドメイン」と呼ぶ。サブ ドメインは「⽀援サブドメイン」と「汎⽤サブドメイン」の2種類に分類される。 なお、当事者の視点によって「コアドメイン(例:ERP)」と「サブドメイン(例:EC)」 が⼊れ替わることに注意。ERP構築企業の視点ではコアドメインはERPで、サブドメインが ECとなるが、逆にEC構築企業の視点ではコアドメインはECで、サブドメインがERPとなる ⽀援サブドメイン 「⽀援サブドメイン」はコアドメインほど重要ではないものの、業務的に特別なものを表す。 例えば、コアドメインの⽀援を⾏う独⾃機能などが該当 汎⽤サブドメイン 「汎⽤サブドメイン」は、業務的に特別ではないが、今回のシステムにおいて必要な箇所 (ドメイン)を表す。例えば認証機能やERPやECなど、極端な場合、交換されたとしても差し ⽀えのない機能が該当する 問題領域 コアドメイン サブドメイン ⽀援サブド メイン 汎⽤サブド メイン 解決領域 ドメインモデル ドメインモデル 境界づけられた モデル化 モデル化
  6. 6 ページ 補⾜:「住所」はエンティティ or 値オブジェクト? 『エヴァンズ本』で「コラム−住所は値オブジェクト? その質問をしているのは誰か?」と いうコラムがあるが、その結論としてはアプリケーションやサービスの種類によって変わっ てくる。以下の各ケースでドメインモデルとしての住所の扱いが変わる ECサービスにおける配送先としてのユーザの住所は、住所そのものが識別⼦を持って、⼀意

    である必要はない(家族が同じ住所を登録しても問題ない)ため、値オブジェクトになる ECサイトにおける住所 ユーザ 住所(VO) ECサイトのユーザエンティティ内の住所 (値オブジェクト) ⼀⽅郵便サービスでは、「住所」⾃体がドメインの中⼼的な存在で個々の住所が、⼀意で識 別される必要がある。また郵便番号との紐付けや、地域の再編で市区町村名が変更になる可 能性があるため、エンティティとして扱う必要がある。 郵便サービスにおける住所 住所 識別⼦ 郵便番号 市区町村 番地 郵便サービスの住所 (エンティティ) 電⼒サービスでは、「住所」は電線と電⼒サービスの⽬的地に相当し、⼀意に識別される必 要がある(⼀緒に住む家族や同居⼈同⼠が別々に電気の利⽤を申請した場合、重複申請にな らないように提供側の電⼒会社側が検知できる必要がある)ため、ここでも住所はエンティ ティである。但し、独⽴した住所エンティティを作るのではなく、契約住居等のエンティ ティを作ってその属性として住所を持たせる場合は、値オブジェクトとなる。 電⼒サービスにおける住所 契約住居 住所(VO) 電⼒サービスの"契約住居" (※住所は値オブジェクト) 電⼒サービスの住所 (エンティティ) 住所 識別⼦ その他属性 ※エンティティと値オブジェクトの両⽅のパターン
  7. 7 ページ ドメインモデルの⾒つけ⽅(ヒト・モノ・コト) システムの関⼼事(ヒト・モノ・コト)のうち、コトに注⽬すると整理しやすい コト MPM決済 チャージ 送⾦ コト ヒト

    モノ 業務遂⾏の当事者 個⼈、企業(法⼈)、担当者等 ヒトが業務を遂⾏する際の関⼼の対象 商品、サービス、店舗、場所、権利、義務、など 業務活動で起こる様々な事象 予約、注⽂、⽀払、出荷、キャンセル、など 補⾜:ドメインモデルとドメインオブジェクト ドメインモデルとドメインオブジェクトはしばしば混同されることも多いが、異なる概念で あるため、ここでは以下の解釈とする ドメインモデルはドメイン内の異なる要素や関連性、ビジネスルールなどを抽象的に表現す るための概念的なモデル。 ドメインオブジェクトは、ドメインモデルの具体的なインスタンスを指す。ドメインオブ ジェクトはドメインモデルに基づいて作成され、ドメイン内の特定の概念や実体を表現する。 なお、「ドメインモデルは、業務の関⼼事を表現するドメインオブジェクトを集めて体系的 に整理したもの」と説明している書籍もある(この場合は、個々の属性(プロパティ)に該 当するオブジェクトを"ドメインに関するオブジェクト"としてドメインオブジェクトと呼んで いると考えられるが)が、これはエヴァンズ本でもIDDD本でも定義されている話ではなく、 「ではドメインモデル以外にドメインに関するオブジェクトがあるのか?」等と誤解される 可能性もあるため、今回はこの解釈は採⽤しない QR決済サービス補⾜ ⾦融機関⼝座連携 加盟店と店舗 決済代⾏業者 加盟店との間に⽴って決済を代⾏する会社。Fict PAYのような決済事業者の決済を代⾏ なお、決済事業者と決済代⾏業者にグループ等のつながりが無い限りは代⾏業者は決済事 業者の各ユーザ情報には関知しない 「加盟店」はFict PAYに加盟した法⼈、「店舗」は加盟店が実際にQR決済を適⽤する店 舗を指す ⾦融機関の⼝座からのチャージには⾦融機関が提供するネット⼝座振替サービスを⽤い て連携する(登録処理は今回省略)
  8. 8 ページ ドメインモデルの整理:MPM決済② MPM決済の流れ Fict PAYサーバ 加盟店 決済代⾏業者 システム Fict

    PAYアプリ (iOS/Android) ③決済リクエスト ①店頭でQRコードの読み取り ②⾦額を指定 ⑤決済代⾏ ⑥処理結果返却 ⑨処理結果返却 ④決済代⾏処理 リクエスト ⑧決済の記録 ⑦Fict PAY残⾼から 差し引き処理 ※Push通知はスコープ外 以下事前課題資料から抜粋 ドメインモデルの整理:MPM決済① ドメインモデルの整理:MPM決済② ー 加盟店と店舗 補⾜:決済代⾏業者について(PSPとPG) 決済代⾏業者はPSP(Payment Service Provider )とPG(Payment Gateway)の2種類あり、PSPが決済代 ⾏の業者を表し、PGが代⾏システムを指す。必ずしもPSPがPGのみを提供するわけではなく、 PGも会社そのものを指す場合もある(社名にGatewayを冠した企業もある)ため、今回は Pament Gatewayを選択。Pament Gatewayのエンティティに提供事業者の社名や住所、URL等を持 たせる形にした 加盟店 店舗 店舗 店舗 エンティティ エンティティ 決済の種類 CPMとMPMの2種類あり、ユーザ側のQRコードを店舗側が読み取って⾏うCPMが主流 業者の各ユーザ情報には関知しない MPM決済 コト カスタマ 決済代⾏事業者 加盟店 決済額 エンティティ エンティティ エンティティ 以下事前課題資料から抜粋 加盟店は更に店舗に分かれる。実際に決済を⾏うのが、加盟店のオフィスではなく、店舗で おこなわれる(加盟店と店舗が同じの場合、別途店舗も登録しておく) ※加盟店と店舗は別 例) 加盟店:セブンイレブン 店舗:リクルートサウスタワー店
  9. 9 ページ ドメインモデルの整理:MPM決済③ ー 加盟店と店舗契約 ドメインモデルの整理:MPM決済④ ー 決済額 決済額は、Fict PAY

    ⼝座の残⾼から差し引きする ドメインモデルの整理:MPM決済⑤ ー MPM決済 MPM決済は履歴として記録される側⾯が⼤きいので、今回はMPM決済履歴とする ドメインモデルの整理:MPM決済⑥ー MPM決済 今回は⾦融系のため、クライアントからのリクエスト(要求)と、決済代⾏からの 決済の結果も保存するため、モデルとして扱う 加盟店 加盟店契約 店舗 店舗契約 契約は加盟店や店舗 のオブジェクト内部 に含めるかは任意 エンティティ エンティティ エンティティ エンティティ MPM決済 エンティティ MPM決済履歴 エンティティ エンティティ GW決済履歴 決済要求履歴 加盟店や店舗にはFict PAYとの契約が存在する Fict PAY⼝座 決済額 エンティティ
  10. 10 ページ 各ドメインモデルのデータと振る舞い(MPM決済) ※値オブジェクトはソースコードを参照 カスタマ ドメインモデル種別 エンティティ(集約) 識別⼦ カスタマシステムID /

    カスタマ⼝座ID 主なデータ ・Fict PAYユーザとしてのカスタマの各情報 ・Fict PAY⼝座オブジェクト 主な属性 ・各アクセサ ・カスタマの状態(有効性)のチェック ・残⾼を更新する(決済額分を差し引く) ・現在の残⾼を返す Fict PAY⼝座 ドメインモデル種別 エンティティ 識別⼦ Fict PAY⼝座システムID / Fict PAY⼝座ID 主な属性 ・Fict PAY⼝座システムID / Fict PAY⼝座ID(識別⼦) ・Fict PAYアカウントの各種情報 主な振る舞い ・各アクセサ ・現在の残⾼を返す ・残⾼を更新する(決済額分を差し引く) ・現在の残⾼を返す 決済要求履歴 ドメインモデル種別 エンティティ 識別⼦ 決済要求システムID 主な属性 ・カスタマの決済要求履歴情報 主な振る舞い ・各アクセサ 加盟店 ドメインモデル種別 エンティティ 識別⼦ 加盟店システムID / 加盟店ID 主な属性 ・加盟店としての各情報 主な振る舞い ・各アクセサ ・加盟店の状態(有効性)のチェック 店舗 ドメインモデル種別 エンティティ 識別⼦ 店舗システムID 主な属性 ・店舗としての各情報 ・加盟店システムID 主な振る舞い ・各アクセサ ・店舗の状態(有効性)のチェック 決済代⾏事業者 ドメインモデル種別 エンティティ 識別⼦ 決済代⾏業者システムID 主な属性 ・決済代⾏業者(GW)としての各情報 主な振る舞い ・各アクセサ ・GWの接続URLを返す ・決済代⾏業者(有効性)のチェック 決済履歴 ドメインモデル種別 エンティティ 識別⼦ 決済履歴システムID / 決済履歴ID 主な属性 ・カスタマの決済履歴情報 ・決済要求システムID ・決済代⾏履歴システムID 主な振る舞い ・各アクセサ GW決済履歴 ドメインモデル種別 エンティティ 識別⼦ 決済代⾏履歴システムID 主な属性 ・GW決済(決済代⾏)履歴情報 主な振る舞い ・各アクセサ
  11. 11 ページ ドメインモデルの整理:チャージ① 以下事前課題資料から抜粋 ドメインモデルの⾒つけ⽅(チャージ) 残⾼チャージの流れ Fict PAYサーバ ⾦融機関(⼝座) Fict

    PAYアプリ (iOS/Android) ③チャージ リクエスト ①⾦融機関を選択 ②⾦額を指定 ⑤処理結果返却 ⑧チャージ結 果返却 ④⼝座振替リク エスト ⑥Fict PAY残⾼へ のチャージ ⑦チャージの記録 ※Push通知はスコープ外 コトのチャージを中⼼にざっくり以下の形に整理 ドメインモデルの整理:チャージ② ドメインモデルの整理:チャージ③ ー⾦融機関と⽀店 ⾦融機関には、⽀店の概念があるので、⽀店もドメインモデルとして作成しておく エンティティ エンティティ ⾦融機関⽀店 ⾦融機関 チャージ額は、決済額は、Fict PAY ⼝座のFict PAY残⾼から差し引きする 。FictPay残⾼は⾦融機 関の⼝座からチャージする。⼝座⾃体はFict PAYの外部であるものの、参照するためにFict PAY内 部で⾦融機関の情報が必要 チャージ コト カスタマ チャージ額 ⾦融機関⼝座 エンティティ エンティティ エンティティ エンティティ ⾦融機関システム チャージ額 ⾦融機関 ⾦融機関⼝座 (Fict PAY内) ⼝座振替依頼 Fict PAY⼝座 ⾦融機関⼝座
  12. 12 ページ 各ドメインモデルのデータと振る舞い(チャージ) ※値オブジェクトはコードを参照 ドメインモデルの整理:チャージ④ー 履歴 今回は⾦融系のため、クライアントからのリクエスト(要求)と、⾦融機関からの ⼝座振替結果も保存するため、モデルとして扱う エンティティ エンティティ

    ⼝座振替履歴 チャージ要求履歴 チャージ要求履歴 ドメインモデル種別 エンティティ 識別⼦ チャージ要求履歴システムID 主な属性 ・カスタマのチャージ要求履歴情報 ・⾦融機関システムID ・⾦融機関⽀店システムID 主な振る舞い ・各アクセサ ⾦融機関 ドメインモデル種別 エンティティ 識別⼦ ⾦融機関システムID / ⾦融機関ID 主な属性 ・⾦融機関としての各情報 主な振る舞い ・各アクセサ ・⾦融機関の状態(有効性)のチェック ⾦融機関⽀店 ドメインモデル種別 エンティティ 識別⼦ ⾦融機関⽀店システムID 主な属性 ・⾦融機関⽀店としての各情報 ・⾦融機関システムID 主な振る舞い ・各アクセサ ・⽀店の状態(有効性)のチェック チャージ履歴 ドメインモデル種別 エンティティ 識別⼦ チャージ履歴システムID 主な属性 ・カスタマのチャージ履歴情報 ・⾦融機関システムID ・⾦融機関⽀店システムID ・チャージ要求履歴システムID ・⼝座振替履歴システムID 主な振る舞い ・各アクセサ カスタマ ドメインモデル種別 エンティティ(集約) 識別⼦ カスタマシステムID / カスタマ⼝座ID 主な属性 ・Fict PAYユーザとしてのカスタマの各情報 ・Fict PAY⼝座オブジェクト 主な振る舞い ・各アクセサ ・カスタマの状態(有効性)のチェック ・残⾼を更新する(チャージ分を⾜す) ・現在の残⾼を返す Fict PAY⼝座 ドメインモデル種別 エンティティ 識別⼦ Fict PAY⼝座システムID / Fict PAY⼝座ID 主な属性 ・Fict PAY⼝座システムID / Fict PAY⼝座ID(識別⼦) ・Fict PAYアカウントの各種情報 主な振る舞い ・各アクセサ ・現在の残⾼を返す ・残⾼を更新する(チャージ分を⾜す) ・現在の残⾼を返す
  13. 13 ページ ⾦融機関⼝座 (Fict PAY内) ドメインモデル種別 エンティティ 識別⼦ ⾦融機関⼝座システムID /

    ⾦融機関⼝座ID 主な属性 ・⾦融機関⼝座としての各情報 ・カスタマシステムID ・⾦融機関システムID ・⾦融機関⽀店システムID 主な振る舞い ・各アクセサ ・⾦融機関の状態(有効性)のチェック 補⾜:Fict PAY残⾼はFict PAY⼝座と切り離すべきかどうか 表題のFict PAY残⾼はエンティティとなるべきかについて、ここでは「残⾼がエンティティに なるのは概念として違和感がある」等の感覚的な判断ではなく、ロジカルに考察すると、結 論としては、「残⾼はFict PAY⼝座から切り離してエンティティとなるべきではない」となる 以下に理由を述べると、まず以下のDDDの原則が挙げられる 【DDDの原則】 ・残⾼は不変ではなく頻繁に増減されるので"変更可能な値"として扱う必要がある。 ・変更可能な値は値オブジェクトではなくエンティティが保持する必要がある ・エンティティは⼀意に特定する"識別⼦"を持つ必要がある しかしながら、Fict PAY⼝座は複数の”残⾼”属性を持つわけではないため、理論上は識別⼦と してFict PAY⼝座ID等を持たせれば残⾼を(Fict PAY⼝座と関連性を持たせた上で)エンティ ティとして切り出すことは可能ではないかという考え⽅もあるが、以下の理由でFict PAY残⾼ を切り出すことは望ましくない 【残⾼を切り出すべきでない理由】 ・残⾼⾃体はQR決済において、⼀意に識別すべき対象ではなく、エンティティにはなり得な い(⼀意であるべきはドメインの中⼼にあるカスタマやFict PAY⼝座である) ・但し残⾼は不変ではないので値オブジェクトとしても切り出せない ・Fict PAY⼝座で保持している”変更可能な値”は残⾼のみであるため、そこから残⾼をわざわ ざ切り離す必要性はない 従って以上の理由からFict PAY残⾼はFict PAY⼝座内の変更可能な属性として扱うべきである 【その他理由】※上記の理由の⽅がDDDとしては重要 ・概念的に⼝座と残⾼を分けて切り出すのは不⾃然 ・銀⾏⼝座と残⾼は強い関係性を持つため、データの整合性を保つ(データ保護)上で同じ エンティティ内にまとめることが重要 識別⼦を持たせる前に、それはそもそも⼀意に識別すべき対象かどうか考えることが重要 (次ページへ続く) ドメインモデル種別 エンティティ 識別⼦ ⼝座振替履歴システムID 主な属性 ・⼝座振替の履歴情報 ・⾦融機関⼝座システムID 主な振る舞い ・各アクセサ ⼝座振替履歴
  14. 14 ページ 補⾜:集約ルートとその配下のエンティティが互いにプロパティで持ち合うことについて 集約ルート配下のエンティティはルートによって管理され、エンティティが集約ルートにプロパ ティとして関連付けられることで、エンティティ間の⼀貫性や整合性を維持することができる。 ただし、ドメインオブジェクトの集約ルートと、その配下のエンティティが互いにプロパティで持 ち合うことについては注意が必要。 集約内のエンティティが互いに参照し合うことで循環参照が発⽣する場合や、過剰な結合が⽣じる 可能性がある場合もあるので、どうしてもプロパティの持ち合いを検討する場合は、適切な設計と モデリングを⾏い、ドメインの要件に応じた関連性を考慮しながら検討することが重要

    集約ルート(エンティティ) 配下のエンティティ 参照 参照 補⾜:集約の考え⽅ 集約は集約ルートにあたるエンティティが他のドメインオブジェクト(エンティティ・値オブ ジェクト)を保持する。整合性を保ちながらデータを更新する単位となるため、ドメインモデ ルの⼀貫性を保つための境界を明確にすることや集約ルートも⼀意な識別⼦を持つことが重要。 集約に対して多くの資料や書籍で様々な説明がされているが、基本的にはID等の識別⼦で参照 されている側が参照する側を集約として保持するものと捉えて差し⽀えない。今回カスタマの 配下にカスタマをシステムIDで参照しているFict PAY⼝座を保持した。なお、⾦融機関⼝座も 保持することは可能だが、チャージ以外には⾦融機関⼝座は絡まない他、処理上の特別なメ リットが無いため、今回はカスタマに⾦融機関⼝座は集約の属性としては持たせなかった ※巨⼤になり過ぎないことが重要。また単なる参照の関係とは異なるので注意(下図) Fict PAY⼝座 Fict PAY残⾼ エンティティ エンティティ?? ⾦融機関⼝座 (Fict PAY内) ⾦融機関 ⾦融機関⽀店 単なる参照の関係 配下のエンティティへ は外部から直接呼び出 し不可 × 外部からの呼び出し はルートを介する 集約ルート カスタマ Fit PAY⼝座 境界 単⼀でもOK
  15. 15 ページ ドメインモデルの⾒つけ⽅(送⾦) 送⾦の流れ Fict PAYサーバ Fict PAYアプリ (iOS/Android) ③送⾦リクエスト

    ①ID検索で送⾦先を指定 ②⾦額を指定 ④送⾦側と送⾦先の Fict PAY残⾼の増減処理 ⑧送⾦・着⾦履歴 返却(任意) ⑥送⾦処理結果返却 ⑦送⾦・着⾦履歴確認(任意) リクエスト Fict PAYアプリ (iOS/Android) ⑤送⾦履歴保存 以下事前課題資料から抜粋 ドメインモデルの整理:送⾦① コトの送⾦を中⼼にざっくり以下の形に整理 送⾦ コト 送⾦者 送⾦額 送⾦先 ドメインモデルの整理:送⾦③ 送⾦額は、Fict PAY⼝座のFict PAY残⾼から差し引きする ドメインモデルの整理:送⾦② 送⾦者と送⾦先は共にカスタマ(送⾦者と送⾦先はエンティティや値オブジェクトとして作 り、その中にカスタマエンティティを⼊れるようなことはしない) 送⾦者 (カスタマ) 送⾦先 (カスタマ) カスタマ カスタマ エンティティ エンティティ Fict PAY⼝座 送⾦額 エンティティ
  16. 16 ページ 各ドメインモデルのデータと振る舞い(送⾦) ※値オブジェクトはコードを参照 Fict PAY⼝座 ドメインモデル種別 エンティティ(集約) ※送⾦者と送⾦先の両⽅分 主なデータ

    ・Fict PAYアカウントの各種情報 ・カスタマオブジェクト ・FictPAY残⾼ 主な振る舞い ・各アクセサ ・現在の残⾼を返す ・残⾼を更新する(送⾦分を⾜し引き) ・⼝座の状態(有効性)のチェック ・現在の残⾼を返す 送⾦要求履歴 ドメインモデル種別 エンティティ 識別⼦ 送⾦要求履歴システムID 主なデータ ・カスタマの送⾦要求履歴情報 主な振る舞い ・各アクセサ 送⾦履歴 ドメインモデル種別 エンティティ 識別⼦ 送⾦履歴システムID 主なデータ ・カスタマの送⾦履歴情報 ・送⾦要求履歴ID 主な振る舞い ・各アクセサ カスタマ ドメインモデル種別 エンティティ(集約) ※送⾦者と送⾦先の両⽅分 識別⼦ カスタマシステムID / カスタマ⼝座ID 主な属性 ・Fict PAYユーザとしてのカスタマの各情報 ・Fict PAY⼝座オブジェクト 主な振る舞い ・各アクセサ ・カスタマの状態(有効性)のチェック ・残⾼を更新する(送⾦先:送⾦分を⾜す) ・残⾼を更新する 送⾦者:送⾦分から差し引く) ・現在の残⾼を返す ・送⾦先が送⾦者本⼈でないことのチェック Fict PAY⼝座 ドメインモデル種別 エンティティ ※送⾦者と送⾦先の両⽅分 識別⼦ Fict PAY⼝座システムID / Fict PAY⼝座ID 主な属性 ・Fict PAY⼝座システムID / Fict PAY⼝座ID(識別⼦) ・Fict PAYアカウントの各種情報 主な振る舞い ・各アクセサ ・現在の残⾼を返す ・残⾼を更新する(送⾦先:送⾦分を⾜す) ・残⾼を更新する 送⾦者:送⾦分から差し引く ・現在の残⾼を返す
  17. 17 ページ Fict PAYサーバ内の全体の構造:MPM決済 MPM決済におけるFict PAYサーバ内での全体の流れと主なモジュールを⽰す PaymentResource 決済代⾏業者 システム 加盟店

    Fict PAYアプリ (iOS/Android) PaymentApplicationService PaymentRecordRepository PgPaymentRecordRepository Fict PAYサーバ Domain Models Controller PaymentRequestRecord Repository Application Service カスタマ Repository PaymentHistoryService Model PgPaymentService Domain Service
  18. 18 ページ ドメインモデル以外の主なモジュールの役割(MPM決済) 以下ドメインモデル以外のモジュールの役割(現時点) ・有効な決済代⾏業者かの確認 ・Gateway(決済代⾏システム)のクライア ントを呼び出してGWへ接続 ・接続後決済代⾏処理を⾏い、PG決済履歴オ ブジェクトを作成 ・PgPaymentRepositoryを呼び出してPG決済履

    歴を保存 PgPaymentService ・1⽇の累積の決済⾦額を返す(チェク⽤に) ・決済履歴の保存 PaymentRecordRepository ・カスタマの有効性チェック (※CustomerRepository呼び出し) ・有効なカスタマをロック付きで返す CustomerService ・有効なカスタマをロック付きで取得 (CustomerServiceを呼び出し) ・有効な加盟店と店舗を取得 (MerchantServiceを呼び出し) ・決済額の単数処理や端数の計算 (計算⽤コンポーネントを呼び出し) ・決済額の妥当性チェック(上限・下限の 確認) (※Fict PAY⼝座・PaymentRecordRepository を呼び出して実⾏) ・決済要求履歴を保存 (PaymentHisotryServiceを呼び出し) ・GWへ接続し、PG決済結果を取得 (PgPaymentServiceを呼び出し) ・PG決済履歴を保存 (PgPaymentServiceを呼び出し) ・決済履歴を保存 (PaymentHistoryServiceを呼び出し) ・決済結果をPaymentResourceに返す PaymentApplicationService MerchantService ・加盟店の有効性チェック (※MerchantRepository呼び出し) ・有効な加盟店を返す PaymentRequestRecord Repository ・決済要求履歴の保存 PaymentHistoryService ・決済要求履歴の保存 (PaymentRequestRecordRepository) ・決済履歴の保存 (PaymentRecordRepository) ・1⽇の累積の決済⾦額を返す (PaymentRecordRepository) PgPaymentRecordRepository ・PG決済履歴の保存 PaymentResource ・決済リクエストをハンドルして、 PaymentApplidationServiceに渡す ・カスタマ端末に決済結果を返す CustomerRepository ・カスタマをロック付きで返す MerchantRepository ・加盟店を返す StoreRepository ・店舗を返す StoreService ・店舗の有効性チェック (※StoeRepository呼び出し) ・有効な店舗を返す
  19. 19 ページ Fict PAYサーバ内の全体の構造:チャージ チャージにおけるFict PAYサーバ内での全体の流れと主なモジュールを⽰す ChargeResouce Fict PAYアプリ (iOS/Android)

    ChargeApplicationService ChargeRecordRepository OnlineDirectDebitRecord Repository Fict PAYサーバ Domain Models Controller ChargeRequestRecord Repository Application Service Domain Service カスタマ Repository ChargeHistoryService Model ⾦融機関(⼝座) OnlineDirectDebitService
  20. 20 ページ ドメインモデル以外の主なモジュールの役割(チャージ) 以下ドメインモデル以外のモジュールの役割 ・1⽇の累積のチャージ⾦額を返す(チェク⽤に) ・チャージ履歴の保存 ChargeRecordRepository ・カスタマの有効性チェック (※CustomerRepository呼び出し) ・有効なカスタマをロック付きで返す

    CustomerService ・有効なカスタマをロック付きで取得 (CustomerServiceを呼び出し) ・有効な⾦融機関と⽀店を取得 (BankServiceを呼び出し) ・チャージ⾦額のチェック ・残⾼のチェック (Fict PAY⼝座呼び出し) ・チャージ要求履歴を保存 (ChargeHistoryServiceを呼び出し) ・ネット⼝振サービスへ接続し、チャー ジ結果を取得 (OnlineDirectDebitServiceを呼び出し) ・Fict PAY残⾼を更新 (Fict PAY⼝座&Repositoryを呼び出し) ・⼝座振替履歴を保存 (ChargeHistoryServiceを呼び出し) ・チャージ履歴を保存 (ChargeHistoryServiceを呼び出し) ・チャージ結果をChargeResourceへ返す ChargeApplicationService BankAccountService ・加盟店の有効性チェック (※MerchantRepository呼び出し) ・有効な⾦融機関を返す ・⽀店の有効性チェック (※BankBranchRepository呼び出し) ・有効な⽀店を返す ・⾦融機関⼝座の有効性チェック (※BankAccountRepository呼び出し) ・有効な⾦融機関⼝座を返す ChargeRequestRecord Repository ・チャージ要求履歴の保存 ChargeHistoryService ・チャージ要求履歴の保存 (ChargeRequestRecordRepository) ・ネット⼝振履歴の保存 (OnlineDirectDebitRecordRepository) ・チャージ履歴の保存 (ChargeRecordRepository) ・1⽇の累積のチャージ⾦額を返す (ChargeRecordRepository) OnlineDirectDebitRecord Repository ・ネット⼝座振替履歴の保存 ChargeResouce ・チャージリクエストをハンドルし て、ChargeApplidationServiceに渡す ・カスタマ端末にチャージ結果を返 す CustomerRepository ・カスタマをロック付きで返す BankRepository ・⾦融機関を返す BankBranchRepository ・⾦融機関⽀店を返す BankAccountRepository ・⾦融機関⼝座を返す OnlineDirectDebitService ・ネット⼝振サービスへ接続し、チャージ 結果を返す
  21. 21 ページ Fict PAYサーバ内の全体の構造:送⾦ 送⾦におけるFict PAYサーバ内での全体の流れと主なモジュールを⽰す RemittanceResouce Fict PAYアプリ (iOS/Android)

    Remittance ApplicationService RemittanceRecordRepository Fict PAYサーバ Domain Models Controller RemittanceRequestRecord Repository Application Service Domain Service カスタマ Repository RemittanceHistoryService Model
  22. 22 ページ DTO(Data Transfer Object)について 事実上の『モデル』として⽤いられることも多いが、厳密にはモデルではない 「オブジェクトはデータと振る舞いを持つ」というオブジェクト指向の原則にも沿っていないため、 あまり多⽤しないことが望ましい ドメインモデル中⼼設計(ドメイン駆動設計)におけるDTOについて ドメイン駆動設計において、DTO(Data

    Transfer Object)は必ずしも必要とされているわけで はなく、DTOはドメインモデルに直接関連するわけではなく、ドメインオブジェクト⾃体が アプリケーションの各レイヤーを通じて直接伝搬することができる場合、DTOをわざわざ使 う必要はない。ただし、実際のアプリケーションでは、複雑なシステムやアプリケーション のレイヤー間のコミュニケーションが複雑になる場合があり、その場合、DTOが便利である ことがある。ただ、データモデル中⼼設計よりも全体としては少なくなる ドメインモデル以外の主なモジュールの役割(送⾦) 以下ドメインモデル以外のモジュールの役割(現時点) ・1⽇の累積の送⾦⾦額を返す(チェク⽤に) ・送⾦履歴の保存 RemittanceRecordRepository ・送⾦者の有効性チェック ・有効な送⾦者をロック付きで返す (※CustomerRepository呼び出し) RemitterService ・有効な送⾦者をロック付きで取得 (RemitterServiceを呼び出し) ・有効な送⾦先をロック付きで取得 (RemitteeServiceを呼び出し) ・送⾦要求履歴の保存 (RemittanceRequestRecordRepository) ・チャージ⾦額のチェック ・送⾦者と送⾦先の残⾼のチェック (Fict PAY⼝座呼び出し) ・送⾦者のFict PAY残⾼を更新 (Fict PAY⼝座呼び出し&Repository) ・送⾦先のFict PAY残⾼を更新 (Fict PAY⼝座呼び出し&Repository) ・送⾦履歴の保存 (RemittanceRecordRepository) ・送⾦結果をRemittanceResourceへ返す Remittance ApplicationService RemittanceRequestRecord Repository ・送⾦履歴要求履歴の保存 RemittanceHistoryService ・送⾦要求履歴の保存 (RemittanceRequestRecordRepository) ・送⾦履歴の保存 (RemittanceRecordRepository) ・1⽇の累積の送⾦⾦額を返す (RemittanceRecordRepository) RemittanceResouce ・送⾦リクエストをハンドルして、 RemittanceApplidationServiceに渡す ・カスタマ端末に送⾦結果を返す CustomerRepository ・カスタマをロック付きで返す ・送⾦先の有効性チェック ・有効な送⾦先をロック付きで返す (※CustomerRepository呼び出し) RemitteerService
  23. 23 ページ ドメインモデル貧⾎症に注意 getter / setterのみのモデルが殆どになるとモデルが単なるデータの⼊れ物になっており、『ドメイ モデル貧⾎症』になっている(モデル中⼼設計になっていない)可能性が考えられる。この場合、 サービス(Application Service, Domain

    Service)の処理を⾒直したり、モデルに責務を与える(モデ ルで出来ることはモデルにやらせる)ことで、貧⾎症を回避できる エンティティのプロパティにOptionalを使⽤することについて ドメイン駆動設計において、エンティティ内の項⽬にOptionalクラスを使⽤することは⼀般的には 推奨されていない。Optionalクラスは、値が存在しない可能性があることを⽰すために使⽤される が、エンティティは通常、必須の属性を持つべき。したがって、エンティティの属性は、nullを許 容せずに具体的な値を持つように設計することが望ましい。Optionalクラスを使⽤すると、属性の 値が存在しない場合でもnullではなくOptional.empty()を返すことになる。これにより、エンティ ティの状態を正しく表現することが難しくなり、コードの可読性や保守性が低下する可能性が考え られる。 代わりに、エンティティの属性には適切なデフォルト値を与えるか、もしくはコンストラクタやメ ソッドの引数で必須の値を受け取る(デフォルト引数)ように設計することが推奨される。これに より、エンティティが常に有効な状態を保つことができる ただし、例外的なケースや特定の要件に応じて、Optionalクラスを使⽤することも考えられる。た だし、その場合でも、Optionalクラスの使⽤は慎重に⾏う必要がある。 エンティティの項⽬を全て値オブジェクトにすべきか エンティティの項⽬を全て値オブジェクト(Value Object)にする必要はなく、最終的な判断は、ド メインの特定の要件やモデリングの⽬的に依存する。 エンティティの項⽬を値オブジェクトにするかどうかは、以下のような要素に基づいて判断するこ とが⼀般的 項⽬の性質と重要性: 項⽬が単純なデータ(数値、⽂字列など)であり、その値⾃体が重要な意味 を持つ場合や振る舞いを持たせることが適切な場合、値オブジェクトとしてモデル化することが適 切 項⽬の再利⽤性と共有性: 項⽬が他のエンティティや値オブジェクトと共有される可能性がある場 合、値オブジェクトとして切り出すと再利⽤することができる。再利⽤性が⾼くなるほど、値オブ ジェクトにする利点が増える。 項⽬の変更頻度: 項⽬が頻繁に変更される可能性がある場合、値オブジェクトにすることで、変更 の影響範囲を制限しやすくなる。⼀⽅、項⽬がほとんど変更されない場合は、エンティティに直接 含める選択も検討すると良い。 ドメインの意図を適切に表現し、モデルの理解やメンテナンスを容易にするために、適切なバラン スを⾒つけることが重要
  24. 24 ページ モデルの配下にRepositoryやDAOを保持することについて ⼀般的には以下の理由でドメインモデルがRepositoryやDAOを直接操作することは推奨されない 1. ドメインの独⽴性の確保: ドメインモデルはビジネスロジックやドメイン固有のルールを表現し、 ドメインの独⽴性を保つための中⼼的な要素で、ドメインモデルがRepositoryやDAOに直接依存する と、ドメインモデルがデータアクセスの詳細(DB)に結びついてしまい、ドメインの独⽴性が損な われる

    2.ドメインモデルのテスト容易性: ドメインモデルがRepositoryやDAOを直接操作すると、ドメイン モデルのテストが困難になる場合がある。データベースや永続化層への依存を持つことで、テスト の際にデータベースへのアクセスが必要となり、テストの実⾏時間が増えたり、テストの作成やメ ンテナンスが複雑になったりする可能性がある 3. 責務の明確化と分離: ドメインモデルとデータアクセスのモジュールは、それぞれ異なる責務を 持つ。ドメインモデルはビジネスロジックやドメイン固有の振る舞いに集中すべきであり、データ アクセスはデータベースや永続化層へのアクセスに特化した責務を持つべきであるため、このよう な分離によって、コードの理解や保守性が向上する ドメインモデルの改善 ドメインモデルはプロジェクトの進⾏や要件の変化に応じて進化する必要があり、ユーザフィード バックや新たなドメインの理解を反映させながら、モデルを改善していくことが重要。 ドメインモデルは常に完璧な状態になるわけではなく、実際の使⽤やフィードバックを通じて洗練 されていくもの。 反復的な改善と進化のためには、以下の⼿法やプラクティスが役⽴つ (次ページへ続く)
  25. 25 ページ 補⾜:イベント駆動アーキテクチャ(DomainEventPublisherとDomainEventSubscriber) イベントソーシング イベント駆動アーキテクチャ(Event-Driven Architecture)は、システムの設計やコンポーネント間 の相互作⽤をイベントに基づいて構築するアーキテクチャ。 発⽣したすべてのドメインイベントを「イベントストア」に格納すると、記録されたイベントを最 初から順番に再⽣すればオブジェクトの状態を復元できるため、監査やバグ調査において⼒を発揮 する。ビジネス的なメリットが必要な場合に採⽤を検討してみる余地があるといえる

    ユーザーフィードバックの収集: 実際のユーザやステークホルダーからのフィードバックを積極的 に収集し、ユーザーのニーズや要件の変化に応じてモデルを修正・改善する モデルの実証: モデルを実際のシナリオや使⽤例に基づいて検証する(予期した通りに機能し、要 件を満たしているかどうかを確認する) ドメインエキスパートとの継続的なコラボレーション: ドメインエキスパートとの定期的なコミュ ニケーションを維持し、新たな洞察やドメインの変化を共有し、モデルの改善に反映させる リファクタリング: モデルの不要な複雑さや冗⻑性を取り除くために、定期的なリファクタリング を⾏う。コードや構造を⾒直し、シンプルで理解しやすいモデルを作り上げる 反復的な改善と進化により、ドメインモデルはより正確で適切なものになり、システムの品質や開 発効率を向上させることが可能になる 補⾜:シナリオとは ドメイン駆動設計の正式な要素ではないものの、サービスが多すぎる場合や、Controllerがスッキリ させる⽬的でシナリオ(Scenario)という概念を⽤いることもある Controller Scenario 引用元:https://zenn.dev/taiyou/articles/7b0325591bf1f8