Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
実践オブジェクト指向設計サブ資料:③ドメインモデル中心設計
Search
Recruit
PRO
August 09, 2024
Technology
3
930
実践オブジェクト指向設計サブ資料:③ドメインモデル中心設計
2024年度リクルート エンジニアコース新人研修の講義資料です
Recruit
PRO
August 09, 2024
Tweet
Share
More Decks by Recruit
See All by Recruit
Azure Functions HTTPトリガーにおけるタイムアウトでハマったこと
recruitengineers
PRO
2
180
実務につなげる数理最適化
recruitengineers
PRO
7
740
うちにも入れたいDatadog
recruitengineers
PRO
2
580
リクルートのデータ基盤 Crois 年3倍成長!1日40,000コンテナの実行を支える AWS 活用とプラットフォームエンジニアリング
recruitengineers
PRO
2
350
Splunk Enterpriseで S3のデータを直接検索してみた!
recruitengineers
PRO
2
160
Looker APIを使い倒す ユーザーフィードバックを基にした継続的改善サイクル
recruitengineers
PRO
3
58
Kaggleふりかえり会〜LLM 20 Questions & ISIC 2024
recruitengineers
PRO
2
240
Balancing Revenue Goals and Off-Policy Evaluation Performance in Coupon Allocation
recruitengineers
PRO
2
52
Flutterによる 効率的なAndroid・iOS・Webアプリケーション開発の事例
recruitengineers
PRO
0
400
Other Decks in Technology
See All in Technology
20241220_S3 tablesの使い方を検証してみた
handy
4
690
成果を出しながら成長する、アウトプット駆動のキャッチアップ術 / Output-driven catch-up techniques to grow while producing results
aiandrox
0
380
怖くない!ゼロから始めるPHPソースコードコンパイル入門
colopl
0
160
Storage Browser for Amazon S3
miu_crescent
1
290
Working as a Server-side Engineer at LY Corporation
lycorp_recruit_jp
0
370
サイバー攻撃を想定したセキュリティガイドライン 策定とASM及びCNAPPの活用方法
syoshie
3
1.4k
TSKaigi 2024 の登壇から広がったコミュニティ活動について
tsukuha
0
170
組み込みアプリパフォーマンス格闘記 検索画面編
wataruhigasi
1
140
Qiita埋め込み用スライド
naoki_0531
0
5.3k
多領域インシデントマネジメントへの挑戦:ハードウェアとソフトウェアの融合が生む課題/Challenge to multidisciplinary incident management: Issues created by the fusion of hardware and software
bitkey
PRO
2
120
[Ruby] Develop a Morse Code Learning Gem & Beep from Strings
oguressive
1
190
LINE Developersプロダクト(LIFF/LINE Login)におけるフロントエンド開発
lycorptech_jp
PRO
0
150
Featured
See All Featured
Typedesign – Prime Four
hannesfritz
40
2.4k
Done Done
chrislema
182
16k
Statistics for Hackers
jakevdp
796
220k
How GitHub (no longer) Works
holman
311
140k
Keith and Marios Guide to Fast Websites
keithpitt
410
22k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
Unsuck your backbone
ammeep
669
57k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Practical Orchestrator
shlominoach
186
10k
Fashionably flexible responsive web design (full day workshop)
malarkey
405
66k
Visualization
eitanlees
146
15k
Automating Front-end Workflow
addyosmani
1366
200k
Transcript
サブ資料③:ドメインモデル中心の設計(ドメイン駆動設計) ドメイン駆動設計について 概要 ドメイン駆動設計(Domain-Driven Design, DDD)は、エリック・エヴァンスによって提唱され たソフトウェアの開発手法および設計手法。 特徴は、ビジネスドメインの複雑さを反映し、ソフトウェアをビジネス要件に対して柔軟か つ効果的に対応できるようにするために、ビジネスドメインの理解と表現に焦点を当て、ソ フトウェアの設計と実装を行う。
ドメインモデル中心の設計(ドメイン駆動設計)のメリット ①機能性を高められる これは、役に立つものを作ること、言い換えると「作ったけど使えない」プロダクトが出来上 がることを避けやすい ②保守性を高めること ⾧期間開発しても機能拡張が容易でありつづけられる、つまり、「技術的負債でどんどん開発 速度が低下する」を避けられやすい。開発者も理解しやすい ドメイン中心のアプローチでは、最初は追加のメンテナンスのオーバーヘッドが発生するが、 時間の経過とともに大きな利益が得られる。 ある時点から、ドメイン中心の手法が複雑さの点でデータ中心のアプローチを追い越し、原則 に準拠したシステムの維持と進化が容易になる。 その理由は、アプリケーションにおいて、問題のドメイン自体が、そこから生成されるデータ よりも重要であるため (次ページへ続く) 1 / 33 ページ
問題領域と解決領域 問題領域 問題領域は、ビジネス上のルール、プロセス、戦略課題等を含んでおり、ソフトウェアが解決す べき実際の問題を分析・明確化する領域 コアドメイン 「コアドメイン」とは事業的に最も重要で戦略的に不可欠なもの 事業的に他と差別化されており、最優先で取り組まれる サブドメイン システム的に必要でありつつもコアドメインではないものを「サブドメイン」と呼ぶ。サブド メインは「支援サブドメイン」と「汎用サブドメイン」の2種類に分類される。
なお、当事者の視点によって「コアドメイン(例:ERP)」と「サブドメイン(例:EC)」が 入れ替わることに注意。ERP構築企業の視点ではコアドメインはERPで、サブドメインがECとな るが、逆にEC構築企業の視点ではコアドメインはECで、サブドメインがERPとなる ドメインとは ドメインとは端的に説明すると対象となる事業が取り扱う世界を表し、独自のルールや文化 が存在する エリック・エヴァンスが唱えたDDDの”原則” 1. 「コアドメインに注力する」こと 2. ドメインの実践者とソフトウェアの実践者による創造的な共同作業を通じて、モデルを探 求すること 3. 明示的な境界付けられたコンテキストの内部でユビキタス言語を語ること 引用元:Domain-centric vs data-centric approaches to software development 2 / 33 ページ
解決領域 問題領域に対する具体的な解決策や技術的な実装を行う領域 支援サブドメイン 「支援サブドメイン」はコアドメインほど重要ではないものの、業務的に特別なものを表す。例 えば、コアドメインの支援を行う独自機能などが該当 汎用サブドメイン 「汎用サブドメイン」は、業務的に特別ではないが、今回のシステムにおいて必要な箇所(ドメ イン)を表す。例えば認証機能やERPやECなど、極端な場合、交換されたとしても差し支えのな い機能が該当する 問題領域
コアドメイン サブドメイン 支援サブド メイン 汎用サブド メイン 解決領域 ドメインモデル ドメインモデル 境界づけられた コンテキスト モデル化 モデル化 ドメイン中心(=ドメインモデル中心)の設計 ドメイン駆動設計では、ソフトウェア開発の中心にビジネスドメインを置く。ドメインはソフ トウェアが扱う対象となるビジネスの知識や課題、ビジネスルール、ビジネスプロセス、業務 内容等を含んだ特定の領域(業界独自の用語や慣習なども含む)を指す。開発者はドメインに ついて学び、ドメインエキスパートと協力し、ドメインを正確にモデル化することに注力する。 ドメイン駆動設計の主な特徴と原則 モデル駆動設計 ドメイン駆動設計では、ドメインモデルを中心とした設計アプローチを採る。ドメインモデル は、ビジネスドメインを表現するための設計要素でビジネス要件を表現し、ソフトウェアの実 装に反映される 3 / 33 ページ
ドメインモデリングの実践方法 業務知識とソースコードを結びつけるために、ユーザ要求をベースにドメインエキスパートと開発 者が対話を重ねてユビキタス言語を見つけながらドメインモデルを作っていく ドメインエキスパート ドメインエキスパートは、担当業務やシステムの対象領域について最も理解した人を指す。CEO や役員、営業、エンジニア等、様々なケース(職種や役職を限定しない)があり、一人ではな く複数人いる場合もある。高度なアプリケーションになるほど、エンジニアの持つ業務知識だ けではシステムに必要な概念を理解したり、表現しきれなくなるため、ドメインエキスパート との議論や擦り合わせが必要になる。なお、近年では専門職として採用するケースも増えてい る
ユビキタス言語 ドメイン駆動設計では、開発にあたってチームやプロジェクト全体でユビキタス言語と呼ばれ る特別な共有言語を開発者とドメインエキスパート(※後述)との間で共有して会話する。 例えば開発現場において以下のことがよく起こり、会話の中で翻訳が発生しがち。 ドメインエキスパート:「顧客」、開発者:「ユーザ」 ドメインエキスパート:「顧客の新規登録」、開発者:「ユーザの新規保存」 ユビキタス言語は、ビジネス要件を正確に表現し、コミュニケーションの障壁を減らす他、そ のままドメインモデルやソースコードとしても実装されるため、注意深く検討する必要がある チームで、ドメインのシナリオとモデルを検討することで、あいまいな要件から明確な仕様へと発 展させていき、ソフトウェア要件を洗練させ、オブジェクトのライフサイクルを意識して、エン ティティの候補を抽出する ドメインモデルとは ドメインモデルは、複雑な業務ドメインの中から、システムに必要な概念を適切に抽出する方法に なる ※そもそもモデルには抽象化によって不要な詳細を省くことで、問題を解決することを表す 4 / 33 ページ
ユビキタス言語のポイント 例えば同じ「商品」でもコンテキストによってニュアンスが変わる そこをドメインエキスパート(企画担当者等)と開発者の認識が合っていることが重要 ドメインモデル中心設計(ドメイン駆動設計)におけるアプリケーションの構成要素 ドメインモデル中心設計(ドメイン駆動設計)における構成要素は以下 ユビキタス言語の重要性 ユビキタス言語が十分に浸透していない、あるいはまったく使われないプロジェクトは常に 翻訳(メンタルマッピング)をしている状況であるといえる。 ドメインエキスパートは技術的な専門用語やシステムのことを理解せず、独自の専門用語で 会話を行い、開発者たちはその言葉を自分たちの言葉へ翻訳することに躍起になる。
両者の対話の中にドメイン駆動設計における重要な概念のヒントになるニュアンスを含んだ 言葉が現れることがあるが、それを見逃さないために、翻訳のコストは極力減らさなければ ならない。そうしないと、開発者たちのドメインに対する理解が中途半端になったり、誤解 したりで、翻訳に支払われる時間とコストは少なくないにも関わらず、最終的には本来のド メインの概念と程遠いオブジェクトが具現化されてしまう コントローラ (プレゼンテーション層) サービス (アプリケーション層) リポジトリ (データアクセス層) ドメインモデル (ドメイン層) プレゼンテーション層 アプリケーション層 (ユースケース層) ドメイン層 データアクセス層 レイヤードアーキテクチャ 5 / 33 ページ
世界 ドメインモデルには大きく分けて一意に特定できるエンティティと一意に区別しない値オブ ジェクトがある ドメインモデルの種類 集約 エンティティ 値オブジェクト 集約は、複数の関連するエンティティ(Entity)や値オブジェクト(Value Object)をグループ化 し、一つのまとまった単位として扱う概念で、通常、ルートエンティティ(Aggregate
Root)と 呼ばれる特定のエンティティをルートとして、配下に他のエンティティや値オブジェクトを保 持する形をとる。集約はドメインモデルの一貫性を保つための境界を定義し、関連するエン ティティや値オブジェクトの内部の状態をカプセル化する。 ドメイン駆動設計において、ビジネスドメインの中心的な存在で一意なものを表現する概念で 一意識別子をもつ。一意であるため、⾧期にわたって変化するオブジェクトになる。例えば 「社員」というエンティティの場合、社員番号で社員を一意に識別することで、同じ人物であ ることを把握し、住所や所属といった属性を変更できる。なお、一意に識別して変更を管理す る必要がないものは後述の「値オブジェクト」になる エンティティはプロパティの変更を許容するが、識別子は不変(エンティティは識別子を下記 で述べる値オブジェクトとして保持することが多い)であることが重要 値オブジェクトは一意に識別する必要を持たないオブジェクトを指す。例えば、「社員」エン ティティは「社員ID」を識別子として一意に識別する必要があるが、「社員ID」そのものは一意 な識別子を必要としないため、値オブジェクトに分類される。そのため、「社員」エンティ ティのプロパティのうち、社員IDは数値型(int型等)やString型を使うのではなく、EmployeeId 型やPhoneNumber型を作ることで、ドメインの業務をソースコードでわかりやすく示すことが できる。また、値オブジェクトはエンティティとは異なり、内部で保持する値は変更不可 (Immutable)であるのが原則。つまり一意に識別する必要のないオブジェクトは原則不変であ ること。なお、通常エンティティが値オブジェクトを保持することが多いが、 逆に値オブジェ クトがエンティティを保持することは殆どない 6 / 33 ページ
ドメインモデル貧血症に注意(DDDでよくある失敗) データモデリングに慣れ親しんだエンジニアが、ドメイン駆動設計を始めてみると、データの格納方 法ばかりに気になってしまうことがよくある。 このようなERモデリング(テーブル構成とリレーション関係)を先に検討されたエンティティは、DB 項目のプロパティしか存在しない機能不足なモデル「ドメインモデル貧血症」になりがち getter / setterのみのモデルの多くが、モデルが単なるデータの入れ物(DTO)になっており、『ドメイ モデル貧血症』になっている(モデル中心設計になっていない)可能性が考えられる。この場合、 サービス(Application
Service, Domain Service)の処理を見直したり、モデルに責務を与える(モデルで 出来ることはモデルにやらせる)ことで、貧血症を回避できる 補足:「住所」はエンティティ or 値オブジェクト? 『エヴァンス本』で「コラム-住所は値オブジェクト? その質問をしているのは誰か?」とい うコラムがあるが、結論としてはアプリケーションやサービスの種類によって変わってくる。 以下の各ケースではドメインモデルとしての住所の扱いが変わる ECサービスにおける配送先としてのユーザの住所は、住所そのものが識別子を持って、一意で ある必要はない(家族が同じ住所を登録しても問題ない)ため、値オブジェクトになる ※但し一人のユーザが複数の住所を登録できる場合は、識別する必要がある ECサイトにおける住所 ユーザ 住所(VO) ECサイトのユーザエンティティ内の住所 (値オブジェクト) 一方郵便サービスでは、「住所」自体がドメインの中心的な存在で個々の住所が、一意で識別 される必要がある。また郵便番号との紐付けや、地域の再編で市区町村名が変更になる可能性 があるため、エンティティとして扱う必要がある。 郵便サービスにおける住所 住所 識別子 郵便番号 市区町村 番地 郵便サービスの住所 (エンティティ) 7 / 33 ページ
電力サービスでは、「住所」は電線と電力サービスの目的地に相当し、一意に識別される必要 がある(一緒に住む家族や同居人同士が別々に電気の利用を申請した場合、重複申請にならな いように提供側の電力会社側が検知できる必要がある)ため、ここでも住所はエンティティで ある。但し、独立した住所エンティティを作るのではなく、契約住居等のエンティティを作っ てその属性として住所を持たせる場合は、値オブジェクトとなる。 電力サービスにおける住所 契約住居 住所(VO) 電力サービスの"契約住居" (※住所は値オブジェクト)
電力サービスの住所 (エンティティ) 住所 識別子 その他属性 ※エンティティと値オブジェクトの両方のパターン 補足:列挙型(Enum)はエンティティ?値オブジェクト? 列挙型(Enum)の扱いついて 一意な識別子があれば、エンティティと解釈できる Enumは不変(Immutable)でもエンティティ? 不変なオブジェクトでも識別子があればエンティティ 引用元: https://www.bing.com/ck/a?!&&p=99eac0d029b41de6JmltdHM9MTcxOTE4NzIwMCZpZ3VpZD0wNTIzYTk5Ny02N2M0LTYzNDUtMmI 1NS1iODIxNjYyZTYyZDcmaW5zaWQ9NTIwNA&ptn=3&ver=2&hsh=3&fclid=0523a997-67c4-6345-2b55- b821662e62d7&psq=Vladimir+Khorikov+enum+value+object&u=a1aHR0cHM6Ly9raG9yaWtvdi5vcmcvcG9zdHMvMjAyMS0wOS0wNi 1lbnVtLWVudGl0eS12YWx1ZS1vYmplY3Qv&ntb=1 8 / 33 ページ
補足:集約ルートとその配下のエンティティが互いにプロパティで持ち合うことについて 集約ルート配下のエンティティはルートによって管理され、エンティティが集約ルートにプロパティ として関連付けられることで、エンティティ間の一貫性や整合性を維持することができる。 ただし、ドメインオブジェクトの集約ルートと、その配下のエンティティが互いにプロパティで持ち 合うことについては注意が必要。 集約内のエンティティが互いに参照し合うことで循環参照が発生する場合や、過剰な結合が生じる可 能性がある場合もあるので、どうしてもプロパティの持ち合いを検討する場合は、適切な設計とモデ リングを行い、ドメインの要件に応じた関連性を考慮しながら検討することが重要 集約ルート(エンティティ) 配下のエンティティ
配下のエンティティ(オブジェクト)の参照 集約ルートのエンティティ(オブジェクト)の参照 補足:ドメインモデルが親子で識別子を重複保持することについて 識別子の重複保持について 識別子は親エンティティと子エンティティで被っていても良い 以下は『実践ドメイン駆動設計』の例:親と子でのbacklogItemIdを重複保持している 親(BackLogItem) 子(Task) 9 / 33 ページ
ファクトリ(Factory)の活用 ドメインオブジェクトの生成処理はファクトリ化して一か所にまとめておく 可読性も上がり、保守性や拡張性も向上する (※GRASP原則の「生成者」にも該当) 補足:集約の考え方 集約は集約ルートにあたるエンティティが他のドメインオブジェクト(エンティティ・値オブ ジェクト)を保持する。整合性を保ちながらデータを更新する単位となるため、ドメインモデ ルの一貫性を保つための境界を明確にすることや集約ルートも一意な識別子を持つことが重 要。集約に対して多くの資料や書籍で様々な説明がされているが、基本的にはID等の識別子で参 照されている側が参照する側を集約として保持するものと捉えて差し支えない。例えば以下の
QR決済サービスのドメインモデルでは、顧客の配下に顧客をシステムIDで参照しているQR決済 口座を保持した。なお、金融機関口座も保持することは可能だが、通常QR決済にお艇は、 チャージ以外には金融機関口座は絡まない他、処理上の特別なメリットが無いため、以下の例 では顧客に金融機関口座は集約の属性としては持たせなかった ※巨大になり過ぎないことが重要。また単なる参照の関係とは異なるので注意(下図) 金融機関口座 金融機関 金融機関支店 単なる参照の関係 配下のエンティティへ は外部から直接呼び出 し不可 × 外部からの呼び出し はルートを介する 集約ルート 顧客 QR決済口座 境界 単一でもOK 10 / 33 ページ
ドメイン駆動設計におけるサービスについて アプリケーションサービス (Application Service) ・アプリケーションサービスは、ユーザーの操作やシステムのトランザクション全体にわた るワークフローやプロセスを調整・管理します。 ・具体的には、ビジネスプロセスをオーケストレーション(組織化・調整)する役割を持ち、 複数のドメインオブジェクトやドメインサービスを利用して、エンドツーエンドのユーザー ケース(例:商品購入、アカウント登録)を実行します。 ・通常、アプリケーションサービスはドメインの複雑なビジネスロジックを直接扱うよりも、
ドメイン層のオブジェクト間のインタラクションや外部アプリケーションとのインテグレー ションを担います。 ・アプリケーション層に配置される ・ドメインモデルとドメインサービスのクライアント ・アプリケーションサービスは状態を持たない ドメインサービス (Domain Service) ・ドメインサービスは、複雑でモデル内のエンティティや値オブジェクトに適切に配置でき ないビジネスロジックをカプセル化する ・ドメイン層に配置 ・エンティティにも値オブジェクトにも適さないドメインのビジネスロジック ・エンティティや値オブジェクトの責務ではないドメインモデルのロジック(複数のドメイ ンオブジェクトを使って計算する処理やファサード)を持つ ・ドメインサービスは状態を持たず、トランザクションの責務も担わない ドメインサービスはユビキタス言語で表現されることがポイント ドメインに関係のないものまで書けてしまうのでトランザクションスクリプトにならないよ うに注意すること (次ページへ続く) ドメイン駆動設計には、「エンティティ」、「値オブジェクト」、「集約」といった「ドメ インオブジェクト」だけではなく、それらの外に記述した方が良いロジックも存在する。 そのようなロジックを記述する際に使用するのが、状態を持たないステートレスな「サービ ス」でサービスには、大きく分けて、アプリケーションサービスとドメインサービスの2つが 存在する 11 / 33 ページ
ドメインサービス作成のポイント ドメインサービスは上記の通りユーティリティやヘルパークラスではない ドメインサービスの注意点 ドメインサービスはユビキタス言語で表現されることがポイント ドメインに関係のないものまで書けてしまうのでトランザクションスクリプトにならないよ うに注意すること ドメインサービスに状態を持たせないこと モデルのビジネスロジックについて:データ中心設計との違い データ中心設計(トランザクションスクリプト)ではサービスがビジネスロジックを担う ドメインモデル中心設計(ドメイン駆動設計)ではドメインモデルが主にビジネスロジックを担
う ドメインサービスとユーティリティクラスの違い ドメインサービスは以下の点でユーティリティクラスとは異なるの注意 スコープと目的 ユーティリティクラスは一般的にアプリケーション全体で再利用可能な非常に汎用的なヘル パーメソッドを提供する。これに対し、ドメインサービスは特定のドメインに固有のロジッ クや振る舞いを扱い、しばしばドメインのエンティティやオブジェクトと密接に関連する ドメイン知識 ドメインサービスはドメインモデルの一部として、特定のビジネス知識やルールを反映した 設計が求められる。これにはドメインの専門知識を必要とし、実装においてもドメインの文 脈に深く根ざしている プレゼンテーション層 アプリケーション層 (ユースケース層) ドメイン層 データアクセス層 アプリケーションサービス ドメインサービス 12 / 33 ページ
ドメインイベントについて 異なるコンテキストをまたがり、通知するような場合に使用される バッチ処理を劇的に変える可能性がある リポジトリ:ドメインモデル中心設計におけるデータアクセス ドメインモデル中心設計設計(ドメイン駆動設計)において、データアクセスはリポジトリに よって抽象化する リポジトリ リポジトリは、ドメインモデルとデータベース(または永続化層)の間の仲介役で、ドメインモ デルに対するデータアクセスの抽象化を提供し、ドメインモデルの永続化や取得、更新などの操 作を行う。通常、集約とリポジトリは1対1になる
DAO リポジトリとDAOの違いと併用について DAO(Data Access Object)はデータアクセスのためのパターンとして考案されたデザインパ ターンの一つで、データベースや永続化ストレージとの直接的なやり取りを担当し、デー タのCRUD(作成、読み取り、更新、削除)などの操作を提供する。ドメイン駆動設計を構 成する正式な要素ではないものの使用されることもある リポジトリとDAOは似ているものの、考案されたパラダイムが異なるため、比較が難しいが、 併用する場合は、RepositoryがDAOを利用してデータの永続化や取得を行う構造となる。 Repositoryはドメインモデルに対するインターフェースを提供し、ドメインモデルを操作す る際にはRepositoryを介してデータアクセスを行い、内部的にはrepositoryがDAOを使用して データの保存や検索、変更などを行う 補足:シナリオとは ドメイン駆動設計の正式な要素ではないものの、サービスが多すぎる場合や、Controllerがスッキリさ せる目的でシナリオ(Scenario)という概念を用いることもある Controller Scenario 13 / 33 ページ
補足:ドメインイベントの発行場所 イベントソーシングの概要 ドメインイベントの発行は基本は集約ルート(エンティティ)内で行う インベントソーシングの概要図 集約がイベントを発行し、それを保存して、モデルの状態変更の追跡に利用する。リポジトリが イベントストアからイベントを読み込み、それを適用して集約の状態を復元する ※今回はイベントストアは省略 ソースコード例 14 /
33 ページ
ドメインモデルは全体と部分を行ったり来たりしながら作っていく ドメインモデルは全体と部分を行き来しながら、広げたり掘り下げたりしながら作る。これはデー タモデリングのトップダウンアプローチとボトムアップアプローチと同じ 今回の課題の確認 架空のECサイトR書店(仮)のアプリケーションを設計・実装する 最低限の機能 ・商品表示 ・カート追加 ・注文 例:Webコラボレーション製品のコンテキストマップ
コンテキストの分割(境界づけられたコンテキスト) 境界づけられたコンテキストとは、ビジネス上の意味のある単位で、ドメインモデルを分割 するために使用されるコンテキスト(ビジネスドメインを包括する特定の範囲や境界を定め た概念)。特定のモデルが一貫した定義や表現で適用できる範囲でもあり、ビジネスドメイ ン毎にコンテキストを分割する 境界づけられたコンテキスト 引用元:https://www.slideshare.net/slideshow/ss-137608652/137608652 15 / 33 ページ
ユースケースとビジネスフロー(仮) 今回のシステムのアクター ・顧客 ・サイト管理者 ・配送センター管理者 ・配送会社担当者 R書店のユースケース R書店のビジネスフロー ※業務フロー(Figma参照)も要確認 16
/ 33 ページ
顧客のサイト訪問から購入までの流れ ドメインモデリングの流れ (1) ソフトウェア要件の理解 (2) モデリングを検討しエンティティを抽出 (3) エンティティを識別する属性と振る舞いを検討 (4) 「一意な識別子」の設計(今回はIdGeneratorを使用)
(5) エンティティの振る舞い(メソッド)を検討 (6) エンティティの作成方法(コンストラクタ/ファクトリ)を検討 (7) エンティティのバリデーションを検討 今回のシステムでは顧客の行動として以下を想定 1. R書店のWebサイトを訪れる 2. 商品(書籍..etc)を検索/閲覧する 3. 購入希望商品をショッピングカートに入れる 4. アカウントを登録する 5. 配送先を登録(確認)する 6. 支払い方法を登録(確認)する 7. レジに進む 8. 商品代、配送料、配送予定日等を確認して注文を確定する 9. R書店より確認メールが届く(※メールは今回はMockで対応) 10. 配送予定日に商品が届く 11. クレジット会社から引き落としされる 12.クレジットの明細を確認する(引き落とし額を確認する) 17 / 33 ページ
ドメインモデリング(商品情報の検索と表示) 店舗関連のユーザ要求の整理 要求(やりたいこと) ドメインエキスパート側の要求 ・顧客は認証してもしなくても各商品を閲覧することができる ・商品には出版社や著者、価格等の情報を持っている ・商品情報はサイト管理側で登録や更新ができる ・商品は今回は書籍以外のCD/DVDも扱いたい ・商品は在庫に関しての情報も表示したい(残り1点です等) エンジニア側の考慮
・「更新」出来るということは値オブジェクトではなくエンティティの候補になり得るので注意 ・商品種別で管理した方が良いか ・カテゴリーは必要では?(※今回はカテゴリは使用しない) ・商品毎にモデルを分けると表示ページの管理が大変になるので、商品情報はいったん単体で管理 した方がよさそう ・配送センターでの業務(商品情報と在庫の管理が別になる)も考慮すると、商品の物理在庫のモ デルと商品モデルを分けた方が望ましいのでは(そもそも商品そのものの情報と物理的な在庫は別 概念)? 顧客が商品を検索し、閲覧できること 整理された要件と抽出されたエンティティ ・顧客は認証してもしなくても各商品を閲覧することができる ・商品は出版社や著者と紐づいている ・今回は ・サイト管理側で登録や更新する際の商品モデルは店頭表示の商品と分ける(別途定義) ・商品は書籍以外のCD/DVDも扱うための商品種別を持っている ・今回、カテゴリーは使用しない ・商品は単体の商品モデルで管理 要求分析(ドメインエキスパートとの対話) 要件の整理とエンティティの抽出 18 / 33 ページ
ドメインモデリング(カートへの商品追加) 要求(やりたいこと) 顧客は商品画面のボタンを押してカートに追加できる ドメインエキスパート側の要求 ・商品詳細ページの「カートに追加」ボタンを押すと追加される ・顧客は自分専用のカートに自由に商品を追加できる ・カートに追加する商品には数量も指定できる エンジニア側の考慮 ・カートに入れる商品の数も扱う必要があるので、カートと商品をそのまま結び付けるの ではなく、顧客毎に商品と数量を扱うモデルがおそらく必要になるな
・注文時に差し引きする必要が出てくるので商品在庫の情報も必要になるな ・カートでは、追加された各商品の単価×数量を元にした総額の計算の算出(及びそのた めのモデルの"振る舞い")も必要になるだろう 整理された要件と抽出されたエンティティ ・カートは顧客と1対1の関係(一人の顧客が複数カートを持つ必要はない) ・カートと商品は1対多の関係 カートと商品を直接つないでも数量を 持たせる必要があるので、カート内の 各商品にあたるエンティティを作成す る必要がある 要求分析(ドメインエキスパートとの対話)Part1 要件の整理とエンティティの抽出 Part1 19 / 33 ページ
ドメインエキスパート側の要求 ・大手ECサイトのように非登録ユーザでもカートに商品を追加したい ※仕様の追加・変更 エンジニア側の考慮 ・カートをユーザ(ユーザID)とは直接結び付けない形がを取らないといけない ・非登録ユーザがカートを使うためにCookieの値を紐づけよう ・Cookieを仕込むタイミングも必要だ ・でも登録した場合はユーザIDと紐づくかたちにしないといけないな ・それを踏まえた専用のモデル(エンティティ)を作る必要が出てきそう 顧客IDの有無をカートCookie値よりも
先にチェックすることで顧客登録済の 場合に余計なCookieをセットされない ロジックを想定 整理された要件と抽出されたエンティティ ・カートは顧客登録済の場合は顧客と1対1で紐づき、非登録の場合はカートCookieに紐づく ・カートCookieの値は未登録ユーザが商品「カートに追加」ボタンを押した際にセットする 想定 ・カートと商品は1対多の関係 要求分析(ドメインエキスパートとの対話)Part2 要件の整理とエンティティの抽出 Part2 20 / 33 ページ
要求(やりたいこと) ドメインモデリング(レジ関連) 顧客は精算(チェックアウト)するためにレジに進む ドメインエキスパート側の要求 ・カートからレジへ遷移する ・レジで支払い方法(クレジットカード)を選択できる ・レジでは合計金額の表示が必要 ・レジでは届け先住所を氏名とともに表示する(複数から選択可) エンジニア側の考慮 ・レジへは、カートの中の商品をそのまま持ってきた方がシンプルだな
・レジの合計金額の計算とカート内の合計金額の計算は同じだな ・レジは複数の住所やクレジットカードを保持する必要があるな 整理された要件と抽出されたエンティティ ・レジは集約としてカート(エンティティ)を包含して合計金額を返す ・レジは顧客も保持 ・レジは住所も複数保持しており、住所と氏名を結合して返す ・レジはクレジットカードも複数保持している 要件の整理とエンティティの抽出 要求分析(ドメインエキスパートとの対話) 21 / 33 ページ
ドメインモデリング(チェックアウト) 顧客はレジで「注文を確定する」ボタンを押して注文と決済を完了する 要求(やりたいこと) ドメインエキスパート側の要求の掘り下げ ・レジで精算完了(注文確定)時に注文情報の生成が必須 ・注文確定時に配送情報も生成する ・注文情報には合計金額を表示する必要がある ・届け先の住所を氏名を含めて表示が必要(配送情報は住所と顧客とも連携) ・発送完了時に決済完了にしたい(それまでは決済”予約”状態) ・購入したらカートの中身は空になる
エンジニア側の考慮 ・注文情報は決済情報と関連しており、且つそれぞれ状況が変化するため、ステータスを 管理する必要があるな ・注文情報は配送情報とも関連しており、配送状況も変化するため、配送もステータス管 理をする必要があるな ・配送情報には届け先の住所が氏名を含めて必要だから住所や顧客とも関連付けが必要に なるな ・配送情報には配送会社や配達日時等の情報が必要だな ・注文したら、カート内商品の整理以外に商品在庫の更新も必要になるな 整理された要件と抽出されたエンティティ ・注文は注文IDを通して決済と配送と関連付けられる ・注文、決済、配送はそれぞれステータスを保持する ・配送会社と配送(情報)は1対多で関連している 要件の整理とエンティティの抽出 要求分析(ドメインエキスパートとの対話) 22 / 33 ページ
今回のドメインモデル(エンティティ)の一覧 Figmaを参照 ※今回は値オブジェクト等は省略 補足:コンテキストの毎のモデルの定義 例えば、同じ「商品」でも異なるコンテキスト下にあれば(必要な情報も異なり)、微妙 に異なる ・顧客が表示する商品 ・在庫担当者が扱う商品 ・カート内の商品 ・配送業者からみた商品
・注文した商品 必要な属性のみを保持し不要なものはなるべく持たせないことも重要。また、後ほど定義 するデータモデル(DBのテーブル)に引っ張られない(依存度が低い)ドメインモデルを 定義する 他にも顧客のドメインモデルも、認証用やプロフィール表示用のコンテキスト毎にドメイ ンモデルを分ける方法も考えられる 今回は商品やユーザをコンテキスト毎に定義した ※但しモノリシックなアプリケーションで同名のクラスで複数定義する場合は、管理の工 夫が必要(パッケージ違いの同名のクラスが複数あると混乱が生じる) 23 / 33 ページ
コンテキストの分割 ・オンライン書店(ドメイン) ・店頭ドメイン(コアドメイン) マイページ、商品ページ含む レジのところで交錯する ・注文サブドメイン(支援サブドメイン) ・決済サブドメイン(支援サブドメイン) ・配送サブドメイン(支援サブドメイン) ・認証・アクセスサブドメイン(汎用サブドメイン) ・ユーザ(管理)サブドメイン(支援サブドメイン)
・商品(管理)サブドメイン(支援サブドメイン) R書店管理者用 ・在庫(管理)サブドメイン(支援サブドメイン) 配送センター用 ・配送会社管理ドメイン(支援サブドメイン) R書店管理者用(今回はスコープ外) (次ページへ続く) 補足:ドメイン駆動設計における要件の定義 ドメイン駆動設計はアジャイル開発を前提(要件を一度で固定せず反復させる)としてい るが、繰り返しの前に、要件定義で大枠をしっかり掴み、設計から実装工程でブレを生じ させないことが大切 R書店のドメイン ビジネス要件や、各ドメインモデルの有効範囲も考慮して境界付けられたコンテキストを 整理する 境界付けられたコンテキストと各サブドメインが一対一と対応しているのが最も望ましい 形式(『IDDD本』p69より) 24 / 33 ページ
注文確定時(チェックアウト)時の処理は複数の境界をまたいだ処理になる 決済(ゲートウェイへのアクセスも含む)と配送(情報の生成)の処理も同時に行う(エラー ハンドリングやその設計重要) 複数のコンテキスト境界をまたがる処理 注文確定時のフロー(詳細はFigamaを参照) 25 / 33 ページ
永続化:ドメインモデル中心設計(ドメイン駆動設計)におけるDB設計 ドメインモデル(エンティティ)から、データモデル(エンティティ)を作る 該当するドメインモデルをグループ化しながら、データモデルを抽出し、関連づける ドメインモデル=データモデルではない 特にモノリシックなアプリケーションではドメインモデルとデータモデルが似ていることも多いが、 ドメインモデルは必ずしもDBのレコード1対1で紐づくものではない(ドメインモデルはDBと紐づいた 概念ではなく、むしろDBとの密な関係性を回避するためにリポジトリに永続化処理をまとめている) ※なお、ドメインモデルのエンティティとデータモデルのエンティティは別 補足:ドメインサービスについて ドメイン層に配置するドメインサービスについては、今回例えば以下があげられる
店舗ドメイン ・ユーザのCookieからカートCookie値を取得するサービス(CartCookieサービス) ドメインモデル中心設計とデータモデル中心設計におけるデータベースの作り方の違い ドメイン駆動設計 先にドメインモデル見つけて(部分と全体を行ったり来たり、・ボトムアップ、パッケージ図 と業務フロー図)からDB作る ※つまりアプリケーションの領域もまたがって設計している データ(モデル)中心設計: 業務分析(業務フロー)等からトップダウン及びボトムアップでDBのエンティティの抽出 データ中心設計とドメイン駆動設計でのモデリングの違い データモデル (DBテーブル) リソース系データモデル(エンティティ) ドメインモデルからデータエンティティを抽出・マッピングする 例)商品のドメインモデルからデータモデル(商品エンティティ)を抽出 <<Entity>> 商品 店頭サブドメイン ドメインモデル <<Entity>> 商品 商品管理サブドメイン 商品エンティティ (products table) データモデル (DBテーブル) 26 / 33 ページ
永続化:リポジトリの作成 リポジトリをインターフェースとして作り、永続化処理を隠蔽する リポジトリを実装したモジュール内でドメインモデルとデータモデル(テーブル)をマッピ ングする 以下はリポジトリの一例 ドメインモデルと永続化処理との隔離 イベント系データモデル(エンティティ) リソース系と同様にドメインモデルからデータエンティティを抽出・マッピングする 例)注文や決済のドメインモデルからデータモデル(注文エンティティ、決済エンティティ)を抽出 <<Entity>>
決済 決済サブドメイン ドメインモデル データモデル (DBテーブル) 決済エンティティ (payments table) <<Entity>> 注文 注文サブドメイン 注文データモデル (orders table) 27 / 33 ページ
紛らわしい概念:ドメインモデルと概念データモデル(DB設計)との違い ドメインモデルと概念データモデルは同じUMLで記述されることが多いため、紛らわしいが、以下 の点で大きく異なる 具体性のレベル ドメインモデルは比較的具体的で、後のソフトウェア開発やデータベース設計に直接つながる構造 を提供する 概念データモデルはより抽象的で、システムに必要なデータの基本的な概念とその関係を明確にす ることに焦点を当てている 用途 ドメインモデルはシステムの具体的なビジネスロジックとオブジェクトの関係を定義する
概念データモデルはより広い視点から情報の構造を整理し、システム設計の初期段階での理解を深 めるのに役立てるもの 補足:レジモデルについて データベースと紐づかないモデル データベースと直接マッピングがなくても、レジというサブドメインで有効なドメインモデ ルといえる(ここがレジをモデルとしないデータ中心アプローチと異なる) 紛らわしい概念:ドメインモデルとActive Recordのデータモデルとの違い Active Recordパターンとの違い ともにモデルがビジネスロジックを保持している点で共通しているが、設計思想が大きく異 なる 柔軟なドメインの表現ができなくなり、永続化に依存した処理でモデルのビジネスロジック が汚染されるのを防ぐためドメインモデルと永続化処理は切り離されている 一方でActive Recordパターンではデータモデルと永続化処理を一体にしており、DB設計の影響 が大きく、柔軟なドメインの表現が難しくなり、トランザクションスクリプトになりやすい 補足:ドメイン駆動設計におけるDTOの使いどころについて UIにドメインモデルを直接渡すかどうか ドメイン駆動設計においてDTOの使用を選択する場合、使用箇所はアプリケーション層になる UIに渡すと、UIとドメインモデルが密結合になるが、DTOを用いるとDTOに詰め替える分の処 理が冗⾧になりがちなため、トレードオフを意識して適宜選択するのが望ましい (次ページへ続く) 28 / 33 ページ
補足:"コレクションオブジェクト"について Productsというドメインモデルは存在するか? エンティティをコレクション(例えばList<Product>等)として保持しているProductsというオ ブジェクトをドメインオブジェクトとしている実装と遭遇する場合がある。これを値オブ ジェクトと捉えようと書いている記事もあるが、下記の問題点がある ・識別子を持っていない(従って集約やエンティティではない) ・一意に識別されるエンティティ(商品)を内包している(それ自体は一意に識別されない 値オブジェクトとしてはおかしい) 識別の必要のないオブジェクトが内部に識別すべきオブジェクトを抱えているのはおかしい エンティティとしてのProductを内包
しているが、識別子がない!? プレゼンテーション層 アプリケーション層 (ユースケース層) ドメイン層 データアクセス層 DTO / DPO データ構造の公開と受け渡しを担当 29 / 33 ページ
補足:エンティティのプロパティにOptionalを使用することについて ドメイン駆動設計において、エンティティ内の項目にOptionalクラスを使用することは一般的には推 奨されていない。Optionalクラスは、値が存在しない可能性があることを示すために使用されるが、 エンティティは通常、必須の属性を持つべき。したがって、エンティティの属性は、nullを許容せず に具体的な値を持つように設計することが望ましい。Optionalクラスを使用すると、属性の値が存在 しない場合でもnullではなくOptional.empty()を返すことになる。これにより、エンティティの状態を 正しく表現することが難しくなり、コードの可読性や保守性が低下する可能性が考えられる。 代わりに、エンティティの属性には適切なデフォルト値を与えるか、もしくはコンストラクタやメ ソッドの引数で必須の値を受け取る(デフォルト引数)ように設計することが推奨される。これによ り、エンティティが常に有効な状態を保つことができる。
ただし、例外的なケースや特定の要件に応じて、Optionalクラスを使用することも考えられる。ただ し、その場合でも、Optionalクラスの使用は慎重に行う必要がある。 ドメインモデルの改善 ドメインモデルはプロジェクトの進行や要件の変化に応じて進化する必要があり、ユーザフィード バックや新たなドメインの理解を反映させながら、モデルを改善していくことが重要(ドメインモデ ルは常に完璧な状態になるわけではなく、実際の運用やフィードバックを通じて洗練されていくも の)。 ドメインモデルの反復的な改善と進化のために、以下の手法やプラクティスが役立つ ユーザーフィードバックの収集 実際のユーザやステークホルダーからのフィードバックを積極的に収集し、ユーザーのニーズや要件 の変化に応じてモデルを修正・改善する モデルの実証 モデルを実際のシナリオや使用例に基づいて検証する(予期した通りに機能し、要件を満たしている かどうかを確認する) ドメインエキスパートとの継続的な対話とコラボレーション ドメインエキスパートとの定期的なコミュニケーションを維持し、新たな洞察やドメインの変化を共 有し、モデルの改善に反映させる ソースコードのリファクタリング モデルの不要な複雑さや冗⾧性を取り除くために、定期的なリファクタリングを行う。コードや構造 を見直し、シンプルで理解しやすいモデルを作り上げる このような反復的な改善とアップデートにより、ドメインモデルはより正確で適切なものとなり、シ ステムの品質や開発効率を向上させることが可能になる 30 / 33 ページ
アーキテクチャの洗練 レイヤードアーキテクチャからヘキサゴナルアーキテクチャへ ドメイン駆動設計の文脈で関連するアーキテクチャとしてクリーンアーキテクチャ(Clean Architecture)が挙がることが多いが、似ているものの、エンティティの概念等は微妙に異な るので注意 プレゼンテーション層 アプリケーション層 (ユースケース層) ドメイン層 データアクセス層
レイヤーアーキテクチャ (DIP使用時) プレゼンテーション層 アプリケーション層 (ユースケース層) ドメイン層 データアクセス層 レイヤーアーキテクチャ ヘキサゴナルアーキテクチャ 31 / 33 ページ
マイクロサービスとドメインモデル中心設計(ドメイン駆動設計) ドメインモデル中心設計(ドメイン駆動設計)はマイクロサービス化と相性が良い 今後コンテキストが何層にも別れ、システムが複雑化した際も整理しやすい 出典: https://www.google.com/url?sa=i&url=https%3A%2F%2Fxtech.nikkei.com%2Fatcl%2Fnxt%2Fmag%2Fnc%2F18%2F020600014%2F1 00800073%2F&psig=AOvVaw3ijDPcMAmxsL8WduJY1aY3&ust=1718168227601000&source=images&cd=vfe&opi=89978449&ved=0 CBAQjRxqGAoTCKiW-8jh0oYDFQAAAAAdAAAAABC5Ag 補足:クリーン・アーキテクチャ ドメイン駆動設計の文脈で関連するアーキテクチャとしてクリーンアーキテクチャ(Clean Architecture)が挙がることが多いが、似ているものの、エンティティの概念等は微妙に異な
るので注意 出典: https://www.google.com/url?sa=i&url=https%3A%2F%2Fblog.tai2.net%2Fthe_clean_architecture.html&psig=AOvVa w2iymSi4ysmgS73ufQSKexf&ust=1718631929414000&source=images&cd=vfe&opi=89978449&ved=0CBEQjRxqFwo TCODh6v-g4IYDFQAAAAAdAAAAABAo 32 / 33 ページ
出典:https://cdn-ak.f.st-hatena.com/images/fotolife/d/dskst9/20190113/20190113175834.png 33 / 33 ページ