見出し画像

DDD(ドメイン駆動設計)の魅力と実践:AIエージェント・戦略的設計から戦術的設計まで


はじめに

複雑なビジネス要件を前に、なかなか思い通りのシステムが作れない――そんな悩みを抱えている開発チームは多いのではないでしょうか。本記事では、ソフトウェアの核心にある問題を“ドメイン”という視点から捉え、より本質的な課題解決へ導く「ドメイン駆動設計(DDD)」の魅力を余すところなく解説しています。エリック・エヴァンスの名著をはじめとする歴史的背景、ユビキタス言語や戦略的・戦術的設計といったキーワード、さらにはAIエージェントとの親和性まで、多岐にわたるトピックを一気通貫で学べるよう構成しました。初学者がつまずきやすいポイントも具体例を交えてわかりやすく解説しているので、これからDDDに挑戦してみたい方はもちろん、すでに導入済みの方でも新たな発見があるはずです。ぜひこの機会に、ドメイン駆動設計の世界を覗いてみてください。

1. DDD(ドメイン駆動設計)の魅力

1.1 DDDのイメージ

ここでは、まだプログラミングを詳しく知らない方でも「DDD(ドメイン駆動設計)」が何を目指しているのかイメージできるよう、3つの具体例を挙げながら解説します。

具体例1: 文化祭の準備

  • 状況: 高校の文化祭で模擬店を出すとき、クラスメイトで「食材の仕入れ」「お金の管理」「チラシの作成」「接客」の役割を分担する。

  • DDDの視点:

    • それぞれの役割(=ドメイン)が明確になるように「誰が何をするか」を共通の言葉で定義し、分担して進めます。

    • みんなが共通言語を使うことで、「食材の仕入れってどうすればいいの?」「何時までにお金を集めればいいの?」といった不明点を素早く理解して、スムーズに作業を進められます。

具体例2: 部活の大会運営

  • 状況: 部活の大会を運営する際、「試合日程の調整」「会場の割り当て」「審判やスタッフの手配」など多くの作業がある。

  • DDDの視点:

    • 「大会運営」という大きなテーマでも、“試合を組む”部分と“会場を押さえる”部分などを分割して考え、それぞれの専門用語や手順を共通認識で整理する。

    • ドメインを分けて考えることで、変更があったとき(急遽コートが使えない、など)にも、「どの役割のどの部分を変えればよいのか」をすぐに特定し、対応がしやすくなります。

具体例3: 学校行事の写真共有サイト

  • 状況: クラスや部活で撮った写真を共有するためのWebサイトやLINEグループを運営するとき、写真の投稿・タグづけ・閲覧などの機能が必要。

  • DDDの視点:

    • 「写真を投稿する」「写真にタグをつける」「写真を検索する」といった機能や用語(ドメイン言語)をみんなで統一して理解し、機能を作り込む。

    • タグの付け方や閲覧権限の設定など、細かい仕様変更があっても、共通言語に沿って設計・開発を進めることで混乱しにくくなります。

これら3つの例からわかるように、DDDは「チームで取り組む複雑な仕事を共通言語で整理し、役割やルールを明確にしながら開発を進める」イメージを持っていただけると理解しやすいでしょう。

1.2 ソフトウェア開発の課題とDDDでの解決法

ここでは、ソフトウェア開発の現場でよく起こる4つの課題に対して、DDDがどのように解決をもたらすかを見ていきます。

課題1: ユーザーの言葉と開発者の言葉がずれて、ユーザーの期待と開発物がずれる

  • 具体的な状況

    • ユーザー(お客様)は「商品を注文したい」と言っているのに対し、開発者は「データの登録画面が必要なんですね」と話すなど、使っている言葉が違い、最終的に期待と違うシステムが出来上がる。

  • DDDでの解決法

    • ユビキタス言語を利用し、ユーザーと開発者が「同じ言葉・同じ意味」で会話できるようにします。

    • たとえば「注文(Order)」「商品(Product)」「在庫(Inventory)」といった重要な言葉を、チーム全員が共通定義しておく。

    • これにより、会話のズレを最小限にし、ユーザーの期待を正しくシステムに反映できます。

課題2: システムが一枚岩の場合、プログラム(機能)が肥大化する

  • 具体的な状況

    • すべての機能を1つの大きなプログラム(いわゆる「モノリス」)に詰め込んでいると、機能の追加・変更があったときにどこを修正すればいいのかわかりにくくなる。

  • DDDでの解決法

    • ドメインとコンテキストを適切に分離する戦略的設計を取り入れる。

    • 「注文管理」「在庫管理」「請求管理」など、分野ごとにシステムを切り分け、モジュールを明確にする。

    • 機能ごとに独立性が高まるため、変更の影響範囲を小さくでき、システム全体が複雑になりすぎるのを防ぎます。

課題3: システムが一枚岩の場合、RDB(永続化層)が肥大化する

  • 具体的な状況

    • 1つの巨大なデータベースに「注文テーブル」「顧客テーブル」「倉庫テーブル」などがすべて詰め込まれ、テーブル数やカラム数が膨大になる。

    • テーブル間の依存関係が増え、変更に弱くなる。

  • DDDでの解決法

    • ドメインとコンテキストごとにデータストレージを適切に分離する。

    • たとえば「受注管理サービス」用のデータベースと「在庫管理サービス」用のデータベースを切り分けるなど、コンテキスト単位でストレージを構成すると、1つの巨大なDBを触らなくても済む場面が増える。

    • 障害の影響範囲も小さくなり、スケーラブルな設計が可能になります。

課題4: 処理主体のトランザクションスクリプトの場合、変化に弱い

  • 具体的な状況

    • 「画面でボタンをクリック → データベースに登録」といった、手続きだけにフォーカスしたコードが増えると、業務ロジックが複雑になった際に変更に対応しづらい。

  • DDDでの解決法

    • 業務を抽象化したドメインモデルを中心に設計する。

    • たとえば「注文(Order)」という概念(エンティティ)に「商品を追加する」「支払いを完了する」などの振る舞いを持たせ、業務ロジックを一元管理する。

    • これにより、仕様変更や要件追加に強く、保守性の高い設計を可能にします。

1.3 DDD導入のメリット

最後に、DDDを導入することによるメリットを3つ紹介します。

  1. 要件の誤解を減らせる

    • ユビキタス言語によって、ユーザーと開発者が同じ言葉でコミュニケーションをとれるようになります。

    • 結果的に「想定と違うシステムができあがる」リスクが大幅に減少します。

  2. 変更に強い設計が手に入る

    • ドメインモデルを中心に機能を整理し、コンテキストごとに分割しておくことで、部分的な変更が全体に及ぶ影響を最小限に抑えられます。

    • ビジネス環境が変化しても、必要な箇所だけを柔軟に変更しやすいのが強みです。

  3. チーム開発がスムーズになる

    • ドメインごとにチームや役割を明確にすることで、開発者同士のコミュニケーションが取りやすくなります。

    • また、コードやデータベースが整理されるので、新しいメンバーが参加するときも把握しやすく、技術的負債を抑えながら開発を続けられます。

1.4 まとめ

以上が第1章「DDDの魅力」の解説です。
次章以降では、DDDの歴史や用語の詳細、実際の設計手法についてさらに深掘りしていきます。
「DDDって何がいいの?」と聞かれたときに、まずは「ユーザーとの言葉のズレが減って、変更に強く、チーム開発がしやすい設計手法だよ」と答えられるようになってもらえたら嬉しいです。

2. DDDの簡単な歴史

2.1 DDD本(2003年)

書籍の概要

2003年に出版されたエリック・エヴァンス(Eric Evans)の著書、

  • Domain-Driven Design - Tackling Complexity in the Heart of Software

  • 日本語版タイトル: 『エリック・エヴァンスのドメイン駆動設計-ソフトウェアの核心にある複雑さに立ち向かう』(和智右桂、牧野裕子 訳・今関剛 監訳)

この本が、いわゆる「ドメイン駆動設計(DDD)」という概念を体系的に示した最初の本です。
エヴァンスはソフトウェア開発の根底にある「複雑さの本質」にアプローチするために、ドメインモデルを中心とした開発手法を提唱しました。以下では、この本で取り上げられるポイントをイメージしやすいように3つの具体例を挙げて解説します。

具体例1: 「ユビキタス言語」の重要性

  • エヴァンスが強調しているのは、「チームが共有する言語」= ユビキタス言語を確立すること。

  • たとえば「在庫(Inventory)」「受注(Order)」「請求(Invoice)」など、ビジネス上の重要な言葉をチーム全体で統一して使う。

  • これにより、コード上の概念とビジネス担当者の言葉が一致し、開発段階での誤解を大幅に減らせるというメリットが指摘されています。

具体例2: コアドメインの特定

  • DDDでは、「プロジェクト全体の中で特に重要な領域 = コアドメイン」を見極めることを推奨します。

  • 例えば「ECサイト」を開発するときでも、「商品管理」「在庫管理」「注文管理」「支払い管理」などの中で、競合他社との差別化を生む部分をコアドメインとして扱い、そこに集中投資する。

  • この本では、コアドメインを重視しないと、プロジェクトが膨大な機能に埋もれて肝心の競争力を失いかねないことが詳しく解説されています。

具体例3: 戦略的設計と戦術的設計

  • エヴァンスは、大きな構造(コンテキスト分割やコアドメインの明確化など)を扱う戦略的設計と、エンティティ・値オブジェクト・リポジトリ・ドメインサービスなどを扱う戦術的設計を区別しています。

  • 例えば、境界づけられたコンテキスト(BC)を意識することで、「配送コンテキスト」「会計コンテキスト」のようにシステム全体を分割し、それぞれでユビキタス言語を適用する。

  • 戦略的設計で大枠をしっかり決め、戦術的設計でドメインモデルを丁寧に作りこむという流れが紹介されています。

2.2 ADDD本(2006年)

書籍の概要

  • Applying Domain-Driven Design and Patterns: With Example in C# and .NET

  • 日本語版タイトル: 『ドメイン駆動 C#プログラマのためのドメイン駆動的用法』 Jimmy Nilsson(長尾高弘 訳・尾島良司 監修)

エヴァンスの著書で示された概念を、.NET/C#による実践例を通して紹介している本です。DDDをC#でどう書くか、どう実装するか、より現場目線で具体的なコードやパターンが示されており、プログラマーにとってわかりやすいアプローチがまとめられています。

具体例1: .NET/C#によるリポジトリ実装

  • リポジトリ(Repository)パターンを、.NETのORM(Entity Frameworkなど)とどう組み合わせるかという実装例。

  • リポジトリを抽象化し、インターフェースとして定義することで、インフラ(DBアクセス)部分を切り離し、ドメインモデルを綺麗に保つ方法を紹介しています。

具体例2: ドメインサービスと値オブジェクト

  • 「計算ロジックをどこに置くか?」「値オブジェクト同士の比較はどうすれば良いか?」といったプログラミングの具体的な疑問をC#のコード例で解説。

  • 例えば「金額を扱うクラス(Money)」を値オブジェクトとして定義し、加減算の演算子オーバーロードをどう実装するか、といった実践的な例が示されています。

具体例3: 過度なアーキテクチャへの注意

  • 本書ではDDDを実践する際に、「過度なレイヤ化」や「オーバーエンジニアリング」に陥らないように警鐘を鳴らしています。

  • エヴァンスの理念を実装に落とすとき、必要以上に複雑な設計をしがちですが、Jimmy Nilssonは「開発チームの生産性と、ビジネスの価値を生む部分にフォーカスせよ」と具体的に助言しています。

2.3 IDDD本(2013年)

書籍の概要

  • Implementing Domain-Driven Design (Vaughn Vernon 著)

  • 日本語版タイトル: 『実践ドメイン駆動設計-エリック・エヴァンスが確立した理論を実際の設計に応用する』(高木正弘 訳)

「エリック・エヴァンスが示した理論をもっと実務に落とし込みたい!」というニーズに応えたのが、Vaughn Vernonの著書です。より大型プロジェクトや複雑なビジネス要件に直面したときのDDD活用法が詳しく解説されています。

具体例1: 戦略的設計の掘り下げ

  • 境界づけられたコンテキスト(BC)やコンテキストマップを実際の組織構造やプロジェクトの編成に合わせてどう適用するかを、より深く解説しています。

  • 大規模な組織では、ひとつのサービスが複数のBCを持つ場合もあり、それらをどうやって整理するかの実務的アドバイスが豊富です。

具体例2: アプリケーションアーキテクチャとの連携

  • ヘキサゴナルアーキテクチャやCQRS、イベント駆動アーキテクチャなど、DDDと相性の良いアーキテクチャパターンの導入事例が多数紹介されています。

  • 単純なレイヤ化だけでなく、「読み取りと書き込みを分ける」「イベントを中心に結合度を下げる」など、実務に直結したヒントが満載です。

具体例3: テストとDDD

  • 「ドメインモデルの単体テストをどう行うか?」「コンテキストごとの集約やエンティティをテストしやすくするには?」といった、テスト駆動開発(TDD)との組み合わせも丁寧に解説されています。

  • ドメインイベントを発行する仕組みのテスト方法など、DDDを実践する上でのテスト戦略の具体例が多く紹介されているのが特徴です。

2.4 2020年代のDDD

最後に、DDDが登場してから約20年が経過した2020年代におけるDDDの位置づけと、新しい潮流を3つ紹介します。

  1. マイクロサービスアーキテクチャ(MSA)との融合

    • 境界づけられたコンテキストをサービスごとに分割する考え方は、マイクロサービス化と親和性が高いとされています。

    • DDDの「ドメイン分割」とMSAの「サービス分割」が合わさることで、大規模システムを効率よく運用できる事例が増えています。

  2. クラウドネイティブ時代のDDD

    • AWSやAzureといったクラウド上でのアプリケーション開発が主流になるにつれ、インフラ構成を柔軟に変更できるようになりました。

    • その結果、ドメインを境界づけられたコンテキストごとに切り分けて、部分的にスケールアウトしたり、サーバーレスに移行したりする手法が普及しています。

  3. DDDの導入支援ツール・フレームワークの充実

    • エリック・エヴァンスの初版執筆当時は、DDD向けのフレームワークが少なかった一方で、現在ではSpring Boot (Java)、.NET Core (C#)、NestJS (TypeScript)などでDDD実践を支援する仕組みが充実。

    • 「戦術的設計」の実装をサポートするライブラリや「ドメインイベント」を取り扱うメッセージングプラットフォームなどが増え、DDDの学習コストが下がりつつあります。

2.5 まとめ

20年近く経った現在でも、DDDは「複雑な業務要件にどう立ち向かうか?」という根本課題に応える強力な指針として多くのプロジェクトで活用されています。今後もソフトウェア開発の世界で重要な存在であり続けるでしょう。

3. DDDとは?

3.1 DDDの定義

DDD(Domain-Driven Design: ドメイン駆動設計)とは、ソフトウェア開発において、ビジネス上の重要な概念(ドメイン)を中心に据えて設計・実装を行う手法です。
従来の「画面や機能を先に考える」アプローチとは異なり、まずはビジネスの本質や複雑な要件(=ドメイン)を深く理解し、そのモデル(=ドメインモデル)を開発者とビジネス担当者が共有言語で作り上げることが特徴です。

ユビキタス言語と戦略的設計と戦術的設計

  1. ユビキタス言語

    • ドメインに関わる用語を、ビジネス担当者・開発者が共通で使うための言語です。

    • 「この画面が~」「ここのテーブルが~」ではなく、ビジネス要件に登場する重要な言葉(例:受注、請求、在庫など)を開発者もそのまま利用して会話やコードに落とし込みます。

  2. 戦略的設計

    • 「どのようにドメインを切り分け、どこに投資の重点を置くか」といった大局的な判断を行う設計です。

    • 具体的には「境界づけられたコンテキスト(BC)をどう分割するか」「コアドメインをどのように見極めるか」などが該当します。

  3. 戦術的設計

    • ドメインモデルを具体的に形にするための技術的な手法です。

    • たとえば、エンティティ・値オブジェクト・リポジトリ・ドメインサービスなどの構成要素を使い、ソースコードレベルでドメインの知識を表現します。

3.2 ユビキタス言語

定義

ユビキタス言語(Ubiquitous Language)とは、

  • ユーザー(ビジネス担当者)と開発者が同じ言葉を使って会話し、

  • その言葉をそのままモデルやコードに反映させることで、

  • 要件の誤解やコミュニケーションの齟齬を減らすことを目的とした“共有言語”
    です。

1つの言葉が複数の意味を持っていたり、同じ概念に違う呼び名をつけたりすると混乱が生じやすいので、「この用語はこう使う」という約束を明確にします。

3つの具体例

  1. ECサイトの例

    • 「カート」「商品」「注文」など、ビジネス上重要な要素に対して、開発チーム全員が意味を共有する。

    • 「カート」は「買い物中に追加したアイテムの一時的な保管場所」など、機能的・業務的に正確に定義し、コードにもCartとして実装。

  2. 在庫管理システムの例

    • 「在庫(Inventory)」「ロット(Lot)」「出荷(Shipping)」「入庫(Receiving)」といった用語を、現場担当者と同じ定義で開発者が使う。

    • 仕様変更の際も用語の意味をそのままソースコードに反映できるため、混乱を防ぐ。

  3. 経理システムの例

    • 「請求書(Invoice)」「仕訳(Entry)」「勘定科目(Account)」など、会計業務特有の言葉をそのまま変換せずにシステム設計へ反映する。

    • 日本語や業界特有の用語は誤解されやすいので、ユビキタス言語としてしっかり定義し、翻訳ミスや意味のぶれをなくす。

3.3 戦略的設計

定義

戦略的設計(Strategic Design)とは、

  • ビジネス全体を鳥瞰して、どのドメインに注力するか(コアドメインの特定)、ドメインごとにどのような境界づけを行うか(境界づけられたコンテキスト)を考えるアプローチ

  • システムを開発するうえで、最適な“区切り”や“範囲”を明確化する設計手法と言えます。

3つの具体例

  1. コアドメインの選定

    • 例)ECサイトでは「支払い」よりも「ユーザーが欲しい商品をスムーズに見つけ購入する体験」のほうが差別化要因となる場合、そこをコアドメインとする。

    • 社内にある既存の「決済システム」は他社サービスを利用しても問題ないならサブドメインに回す、など優先度づけを行う。

  2. 境界づけられたコンテキスト(BC)の分割

    • 例)大規模なシステムを「受注管理」「在庫管理」「出荷管理」という3つのコンテキストに分け、各コンテキスト内でユビキタス言語を定義する。

    • 不要な連携を極力減らし、各コンテキストが独立して進化できるようにする。

  3. コンテキストマップの作成

    • 例)「在庫管理BC」と「出荷管理BC」が相互にどのように情報をやり取りするかを図示する。

    • ダイアグラムを用いて、「どこで連携するか」「どこが他を利用するか(上流・下流の関係)」を明確にすることで、組織間のコミュニケーションミスを減らす。

3.4 戦術的設計

定義

戦術的設計(Tactical Design)とは、

  • ドメインモデルを具体的に表現するための“要素(エンティティ、値オブジェクト、ドメインサービスなど)”やルール、パターンを用いてシステムを設計する手法

  • コードレベルで「ビジネスロジック」をどのように表現するかが中心課題で、細部にわたって設計・実装を行います。

3つの具体例

  1. エンティティ(Entity)

    • 例)「注文(Order)」をIDを持ったエンティティとして扱い、注文日時・注文者・注文商品の集合などの情報や振る舞いをひとまとめに管理する。

    • 戦術的設計ではエンティティのライフサイクルを追う役割が重要。

  2. 値オブジェクト(Value Object)

    • 例)「住所(Address)」や「金額(Money)」を値オブジェクトにし、不変(Immutable)で扱う。

    • 同じ値であれば同一とみなすための等価性の設計や、演算(加算・減算など)を適切に定義する。

  3. ドメインサービス(Domain Service)

    • 例)「在庫引き当て」という操作が特定のエンティティに属さず、複数のエンティティにまたがる場合、ドメインサービスとして実装する。

    • そうすることで、ビジネスルールが散逸しないようにまとめて管理できる。

3.5 まとめ

  • DDD: ドメインを中心に据えた開発思想

  • ユビキタス言語: 全員が同じ言葉を使うための共有言語

  • 戦略的設計: ドメインをどのように分割し、コアドメインをどこに設定するかを決める大局的な設計

  • 戦術的設計: エンティティや値オブジェクト、ドメインサービス等を用いてドメインモデルを具体的に表現する開発実装レベルの設計

DDDは、複雑なビジネス要件と開発手法をつなげる強力なガイドラインです。

4. 戦略的設計

DDDにおいて「戦略的設計(Strategic Design)」は、システム全体を俯瞰し、どのようにドメインを切り分け、どこにビジネス価値を集中させるかを決定するアプローチです。ここでは、ドメイン、コアドメイン・サブドメイン、境界づけられたコンテキスト、コンテキストマップといった概念について解説します。

4.1 ドメイン

定義

  • ドメイン(Domain) とは、ソフトウェアが解決しようとしているビジネス領域や問題領域のことです。

  • たとえば「ECサイトの売買」「在庫管理」「銀行業の決済処理」「医療業の予約管理システム」など、ビジネスごとに固有のルールや手続き、概念が存在します。

  • DDDでは、このドメインの知識を明確にし、ソフトウェアとビジネスの間に生じるギャップを埋めることを重視します。

3つの具体例

  1. ECサイトのドメイン

    • 商品登録、在庫管理、受注処理、配送手配、顧客管理など、ECサイトにおける業務領域がドメインにあたります。

    • たとえば「商品を注文する」行為には、カートへ商品を追加 → 注文情報を確認 → 決済手段を確定 → 在庫を減らす → 配送手配する…といったルールや手順があります。

  2. 銀行業のドメイン

    • 口座開設、振り込み、残高照会、利息計算、ローン審査など、銀行のビジネスで必要とされる業務や知識全般がドメインです。

    • 「口座に入金する」「残高を引き落とす」などは明確なビジネスルールや規制・法的要件が関わるため、その業務知識自体をよく理解していないと正しいシステムを作れません。

  3. 保険業のドメイン

    • 保険契約の申込み、審査、契約維持、保険料計算、保険金請求、支払いなどが一連の業務。

    • 「保険料の計算」は商品種別や契約年齢、支払期間などで変動し、それぞれ独特のビジネスロジックが盛り込まれています。

4.2 コアドメインとサブドメイン

定義

  • コアドメイン(Core Domain) とは、ビジネス価値の源泉となる最重要領域のこと。

    • 開発チームが特に注力すべき領域であり、競合他社との差別化を生む源でもある。

  • サブドメイン(Sub Domain) は、コアドメインを補完するその他の領域のこと。

    • 共通化できる部分や既存ソリューションを流用できる部分はサブドメインとして扱い、優先度を下げることが多い。

3つの具体例

  1. ECサイトにおけるコアドメイン

    • 例)「購入体験の最適化」「在庫管理の高度化」など、企業の差別化要因となる部分がコアドメイン。

    • 一方で「決済手段」や「メール送信機能」は汎用サービスを利用できるため、サブドメインとして扱う。

  2. 製造業システムにおけるコアドメイン

    • 例)工場の「生産ラインのスケジューリング」「品質管理」などがコアドメイン。

    • 「給与計算」「勤怠管理」は多くの企業で共通化しやすい仕組みがあり、外部サービス(SaaSなど)を利用できるためサブドメインとするケースが多い。

  3. 医療システムにおけるコアドメイン

    • 例)「予約枠と医師・スタッフのシフト管理」「診断履歴と検査結果の参照」など、医療独自の複雑ルールが絡む部分がコアドメイン。

    • 「決済部分(医療費のクレジット決済など)」や「患者さんへのメール通知」は一般的な仕組みを利用できるためサブドメインとなりやすい。

DDDの考え方では、開発リソースや時間はコアドメインへ投入し、サブドメインはできるだけ省力化することで、ビジネス価値を最大化できるとされます。

4.3 境界づけられたコンテキスト

定義

  • 境界づけられたコンテキスト(Bounded Context, BC) とは、ドメインモデルを適用する明確な“境界”を示す概念です。

  • システムを複数のコンテキストに分割し、各コンテキストごとにユビキタス言語を定義することで、ドメインモデルの混乱や肥大化を防ぐ狙いがあります。

3つの具体例

  1. 受注管理コンテキスト / 在庫管理コンテキスト

    • ECサイトでは「受注管理(注文を受け付け、顧客への請求情報を作る)」と「在庫管理(商品の在庫数を更新し、在庫切れを防ぐ)」は別のコンテキストとする。

    • 受注管理での「注文」という用語と、在庫管理での「注文」という用語が同じ意味を持たない場合があり、混乱を防ぐために明確に分ける。

  2. 配送管理コンテキスト / 請求管理コンテキスト

    • 「配送管理」は荷物の発送や到着日時の追跡に特化したルールを持ち、一方「請求管理」は請求書の発行や入金確認などの会計ルールを扱う。

    • 違うユビキタス言語が使われるため、別の境界づけられたコンテキストとして分割することで、仕様変更に対する影響範囲を最小化できる。

  3. 顧客管理コンテキスト / マーケティングコンテキスト

    • 「顧客管理」は顧客情報の基本属性や購買履歴などを正確に保持することがミッション。

    • 「マーケティング」はクーポンの発行やメール配信のターゲット選定など、顧客情報を活用する観点が違う。

    • 両者で顧客という用語を扱うが、必要な属性や処理内容が異なるため、コンテキストを分割して独自に最適化を行う。

4.4 コンテキストマップ

定義

  • コンテキストマップ(Context Map) とは、複数の境界づけられたコンテキストの関係性を示す図のこと。

  • 「どのコンテキストが上流・下流か」「どのような連携方式でデータをやり取りするか」「チーム間のやり取りをどうするか」を視覚的に整理します。

3つの具体例

  1. 受注管理(上流) → 在庫管理(下流)

    • 受注管理が「注文が成立した」という情報を発行し、在庫管理が在庫数を調整する。

    • コンテキストマップ上で、上流コンテキストと下流コンテキストを矢印や線で結び、「Event Publisher」「Event Consumer」などの関係性を明確化する。

  2. 配送管理(上流) ↔ 請求管理(下流)

    • 配送が完了すると請求管理が「配送完了通知」を受け取って請求処理を実行する。

    • ただし配送コンテキストでも「請求状況」を参照する必要がある場合、相互連携が必要となるため、コンテキストマップ上で依存を二方向に描き、密結合になりすぎないように設計を考慮する。

  3. 顧客管理(上流) → マーケティング(下流)

    • 顧客管理で最新の顧客リストを保持し、マーケティング側が必要に応じてリストを取得してクーポン施策を実施。

    • コンテキストマップで「顧客管理はデータを提供する立場」「マーケティングはデータを利用する立場」として整理し、データ移行や連携プロセスを可視化する。

コンテキストマップを描くことで、境界づけられたコンテキスト間の依存関係や情報フローが明確化し、開発チーム間のコミュニケーションを円滑にできます。

4.5 まとめ

  • ドメイン: システムが解決しようとするビジネス領域。

  • コアドメイン: ビジネス価値の源泉となる、特に重要な領域。

  • サブドメイン: コアドメイン以外の領域で、共通化や外部サービス利用が可能な領域。

  • 境界づけられたコンテキスト(BC): ドメインモデルを適用する“境界”を明確に区切った単位。

  • コンテキストマップ: 境界づけられたコンテキスト同士の関係性や連携方式を示す地図的な図。

戦略的設計は、広い視点でシステムを俯瞰し、重要な領域(コアドメイン)に力を注ぐための判断材料を提供します。次に予定される「戦術的設計」は、これらのコンテキスト単位で具体的にエンティティや値オブジェクトをどう実装していくか、コードレベルの話へとつながっていきます。

5. 戦術的設計

第4章で紹介した戦略的設計は、ドメインを俯瞰し、システムをどのように分割するかを検討するものです。
対して本章の戦術的設計は、コードレベルでドメインモデルを具体的に表現するための手法です。
ここで紹介する概念(エンティティ、値オブジェクト、ドメインサービス、ドメインイベント、モジュール、集約、ファクトリ、リポジトリ、アプリケーションなど)を上手に組み合わせて実装することで、ビジネスルールをソースコードの中に明確に埋め込み、保守性と可読性に優れたシステムを作り上げます。

5.1 エンティティ

定義

  • エンティティ(Entity) とは、一意のIDを持ち、ライフサイクルを通して同一性を維持するドメインオブジェクトです。

  • 状態(フィールドの値)は変化しても、同じエンティティIDであれば「同一オブジェクト」として扱われます。

3つの具体例

  1. 「注文(Order)」エンティティ

    • orderId(注文ID)を持ち、商品リストや注文日時、配送先などの状態が変化しても、orderIdが同じなら同じ注文として扱う。

    • 注文の「支払い完了」や「キャンセル」といった状態変化をエンティティ内部で表現する。

  2. 「顧客(Customer)」エンティティ

    • ユーザー登録システムにおける顧客情報。customerIdで一意に識別し、名前や住所、連絡先情報が変わっても同じ顧客として扱う。

    • 顧客が引っ越しして住所を変更しても、customerIdで管理することで混乱を回避する。

  3. 「口座(Account)」エンティティ

    • 銀行システムでの口座番号(accountNumber)を一意IDとし、残高や取引履歴など状態が変化しても同じ口座として扱う。

    • 口座振り込みや残高照会などの振る舞いをエンティティ内部に持たせることが多い。

5.2 値オブジェクト

定義

  • 値オブジェクト(Value Object) とは、同じ属性値であれば同一とみなし、一意のIDを持たないドメインオブジェクトです。

  • 不変(Immutable)な設計を推奨し、値が変化する場合は新しい値オブジェクトとして扱うことが多いです。

3つの具体例

  1. 「住所(Address)」値オブジェクト

    • 郵便番号, 都道府県, 市区町村, 番地 を保持し、同じ住所情報なら同一の値オブジェクトとみなす。

    • 住所以外にユニークIDは不要で、変更時は別インスタンスを生成して差し替える設計が望ましい。

  2. 「金額(Money)」値オブジェクト

    • 通貨(円、ドルなど)と金額を保持し、同じ金額なら同一とみなす。

    • 計算や通貨変換などをオブジェクト内部に閉じ込め、金額計算ロジックの重複を防ぐ。

  3. 「期間(Period)」値オブジェクト

    • 開始日時と終了日時を持ち、イベントやキャンペーンなどの有効期間を表す。

    • 値が変わればまったく別の期間になるため、IDによる継続性は必要ない。

値オブジェクトを活用することで、ビジネスルールを小さく分割して高い再利用性や正確性を得られます。

5.3 ドメインサービス

定義

  • ドメインサービス(Domain Service) とは、複数のエンティティや値オブジェクトにまたがるビジネスロジックを表すためのオブジェクトです。

  • 1つのエンティティ内部に入れにくい振る舞いや、複数のエンティティを横断して行う処理をドメインサービスとして切り出します。

3つの具体例

  1. 「在庫引き当てサービス」

    • 注文時に、倉庫内の在庫を適切に探し、在庫数を減らす処理。

    • 「在庫エンティティ」だけで完結しない場合、必要な情報を集めて引き当てロジックを行うドメインサービスを作る。

  2. 「支払い計算サービス」

    • 顧客の注文内容や割引率、クーポンなどを考慮し、最終的な支払額を算出するロジック。

    • 「注文エンティティ」単独では実装が複雑になる場合、別途ドメインサービスで計算処理をまとめる。

  3. 「顧客ブラックリスト判定サービス」

    • 返金履歴やクレーム履歴など複数のエンティティ・値オブジェクト情報を統合し、顧客がブラックリストに該当するかを判定する。

    • このように広範な情報を扱う場合、ドメインサービスとして抽象化すると見通しが良くなる。

5.4 ドメインイベント

定義

  • ドメインイベント(Domain Event) とは、ドメインで重要な出来事が起きたことを表すオブジェクトです。

  • “商品が購入された”“支払いが完了した”“出荷が完了した”など、ビジネス上重要なトリガーをイベントとして扱い、システム全体に伝達することができます。

3つの具体例

  1. 「OrderPlaced(注文が確定した)」イベント

    • 顧客が注文ボタンを押した瞬間に発生。

    • 在庫管理、配送手配などの処理が必要なコンテキストが、このイベントを受け取って後続処理を実行する。

  2. 「PaymentCompleted(支払いが完了した)」イベント

    • クレジットカード決済や銀行振替が完了したタイミングで発行。

    • システム内の別コンテキストが、支払い完了イベントを受け取り、請求ステータスを更新したり、領収書をメール送信したりする。

  3. 「ItemShipped(商品が出荷された)」イベント

    • 倉庫から商品が出荷されたタイミングで発行。

    • 顧客に発送通知メールを送るサービスや、配送状況を管理するサービスがこのイベントをトリガーに動作する。

ドメインイベントを活用すると、イベント駆動アーキテクチャを組み合わせやすくなり、コンテキスト間の結合度を下げることができます。

5.5 モジュール

定義

  • モジュール(Module) は、ドメインモデルをより分かりやすく、管理しやすくするためのパッケージ構造名前空間の分割単位と考えられます。

  • 同じドメインロジックに属するエンティティや値オブジェクト、サービスなどをまとめることで、見通しを良くし、チーム内の責務分担を明確にする狙いがあります。

3つの具体例

  1. 「注文」モジュール

    • Order, OrderLine, OrderService, OrderRepository など、注文に関するドメインオブジェクトを1つのモジュールに集約。

    • 他のモジュールからはOrderServiceの公開メソッドを使って操作するようにし、内部の実装を隠蔽する。

  2. 「在庫」モジュール

    • Inventory, StockItem, InventoryService, InventoryRepository など、在庫管理の要素を1つにまとめる。

    • これにより、在庫管理に関するロジックを一か所に集約し、責務の重複や混乱を避ける。

  3. 「顧客」モジュール

    • Customer, Address, CustomerService, CustomerRepository など、顧客関連をまとめる。

    • 外部から顧客情報を操作する場合は、このモジュール内のサービスを通じて行うようにすることで、カプセル化を強化する。

5.6 集約

定義

  • 集約(Aggregate) とは、エンティティや値オブジェクトを一つのまとまりとして扱う単位です。

  • 集約の中には「集約ルート(ルートエンティティ)」が存在し、外部から集約にアクセスする場合、ルートエンティティを経由して操作します。

  • 集約単位で整合性(トランザクション)を保つという考え方が重要です。

3つの具体例

  1. 「注文(Order)」集約

    • ルートエンティティ:Order

    • 中に含まれるオブジェクト:OrderLine(注文明細), ShippingAddress(配送先)など

    • Orderエンティティを通じて注文明細の追加や配送先の更新を行い、整合性を保つ。

  2. 「顧客(Customer)」集約

    • ルートエンティティ:Customer

    • 含まれる値オブジェクト:Address, ContactInfo など

    • Customerエンティティを通じて住所変更や連絡先変更を行い、顧客情報の一貫性を保つ。

  3. 「在庫(Inventory)」集約

    • ルートエンティティ:Inventory

    • 含まれるオブジェクト:StockItem(品目ごとの在庫数や期限)など

    • Inventoryを通じて在庫の増減を行い、在庫状態の不整合を防ぐ。

適切な集約設計により、トランザクションの境界が明確になり、スケーラビリティや保守性が高まります。

5.7 ファクトリ

定義

  • ファクトリ(Factory) とは、複雑なエンティティや集約の生成ロジックをカプセル化するためのパターンです。

  • コンストラクタで行うには複雑すぎる初期化処理や、外部サービスからの値取得などをファクトリに切り出すことで、コードの責務を明確化します。

3つの具体例

  1. 「注文Factory」

    • 注文を作る際に、クーポンやキャンペーン情報を参照して割引率を計算したり、ユーザー情報を取得して初期状態を決めるなど、複雑な初期化が必要な場合。

    • OrderFactoryのメソッド createOrder(customerId, cartItems) のようにして、新規注文を作成する。

  2. 「アカウントFactory」

    • 口座を開設する際、規約に基づいて初期手数料や残高情報を設定したり、口座番号を自動生成する処理がある場合。

    • AccountFactoryでそれらのロジックをまとめると、アカウント生成の責務が明確になる。

  3. 「在庫Factory」

    • 商品マスタや倉庫情報を参照して、在庫集約を最初に作るときに必要なデータを揃える。

    • InventoryFactoryが複数の外部サービスから情報を取得し、Inventoryオブジェクトを正しく初期化する。

5.8 リポジトリ

定義

  • リポジトリ(Repository) とは、エンティティや集約の永続化(保存・検索・削除)を担当するパターンです。

  • ドメイン層では「どういう操作が必要か」だけを定義し、実際のDBアクセスや外部ストレージへのやり取りはリポジトリが担うことで、ドメイン層からインフラの実装を隠蔽できます。

3つの具体例

  1. 「OrderRepository」

    • findById(orderId), save(order), delete(orderId) などを定義し、注文集約のCRUD処理を行う。

    • 内部でSQLやORMを用いるが、ドメイン層では抽象的なインターフェースのみ参照する。

  2. 「CustomerRepository」

    • findById(customerId), save(customer) などのメソッドを通じて顧客情報の永続化を担当。

    • 「複雑な検索」や「外部APIからの読み込み」が必要な場合も、ドメイン層から見るとシンプルな呼び出しで済むように設計する。

  3. 「InventoryRepository」

    • findByItemId(itemId), save(inventory) などにより在庫集約を取得・保存する。

    • 在庫数の更新をトランザクション管理するなど、整合性を担保する仕組みもリポジトリが担う場合がある。

5.9 アプリケーション

定義

  • アプリケーション(Application) という用語は文脈によりやや異なる意味を持つ場合がありますが、
    DDDの文脈では多くの場合、アプリケーション層(UseCase層) として、

    • ユーザー操作

    • 外部インターフェースからの要求
      を受け取り、ドメイン層にあるモデル(エンティティ・ドメインサービスなど)を組み合わせてユースケースを完遂するレイヤを指します。

3つの具体例

  1. 「注文作成」アプリケーションサービス

    • OrderAppServiceなどの名前で、フロントエンドから「注文確定」リクエストを受け取り、

      1. OrderFactoryで新しいOrderを生成し、

      2. OrderRepositoryに保存し、

      3. 必要に応じてPaymentServiceなどを呼び出し

      4. 結果をUIに返す

    • という一連の処理フローを実装する。

  2. 「顧客登録」アプリケーションサービス

    • CustomerAppServiceで「顧客登録」リクエストを受け取り、

      1. 入力データをバリデーション

      2. CustomerFactoryでCustomerを生成

      3. CustomerRepositoryに保存

      4. 結果をフロントに返す

    • ようなシナリオを分かりやすくまとめる。

  3. 「在庫更新」アプリケーションサービス

    • InventoryAppServiceで「在庫の追加・減少」などの操作を集約し、

      1. InventoryRepositoryから対象在庫集約を取得

      2. Inventoryエンティティのメソッドを呼び出し、在庫を変更

      3. 更新された在庫を再度リポジトリに保存

    • という流れを保証する。

アプリケーション層は、フロント/UIや外部APIとの接合点となり、ドメインロジックを正しく呼び出す役割を果たします。

5.10 分散システム設計〜境界づけられたコンテキストの統合〜

定義

  • DDDの戦略的設計によって複数の境界づけられたコンテキストを分割しても、それぞれのコンテキストはビジネス上の連携やデータ共有を行う必要があります。

  • 分散システム設計は、そのコンテキスト間のデータ連携やイベント連携を、ネットワーク越し(マイクロサービス間など)でどう実装するかを考える領域です。

3つの具体例

  1. イベント駆動でのコンテキスト統合

    • 先に紹介したドメインイベントをマイクロサービス間で発行・購読する形で連携。

    • 「注文コンテキスト」が「OrderPlaced」イベントを発行し、「在庫コンテキスト」がそれを受け取って在庫を引き当てる。

    • それぞれのコンテキストはイベントメッセージ経由で疎結合に統合される。

  2. API連携でのコンテキスト統合

    • あるコンテキストが、必要なデータを取得するために他のコンテキストが提供するREST APIやgRPCなどを呼び出す。

    • 例)「配送コンテキスト」が「顧客コンテキスト」のAPIを叩いて、配送先住所の最新情報を取得する。

    • 過度に直接的な依存が生じないよう、公開するAPIの責務やバージョン管理を丁寧に設計する必要がある。

  3. CQRSを活用した読み取り専用同期

    • 「顧客情報」は顧客管理コンテキストが正としつつ、マーケティングコンテキストで頻繁に参照が必要な場合、イベントを受け取って読み取り用DBに同期しておく。

    • マーケティング側は自身のDBに保存されたデータを参照するだけなので、顧客管理コンテキストへの都度のAPIコールを減らし、スケーラビリティを確保できる。

分散システム設計はネットワーク障害や整合性の問題に留意が必要ですが、DDDの境界づけられたコンテキスト同士を連携させる要として非常に重要です。

5.11 まとめ

  • エンティティ

    • 一意のIDを持ち、ライフサイクルで同一性を維持するドメインオブジェクト。

  • 値オブジェクト

    • 同じ値なら同一とみなし、IDを持たないオブジェクト。

  • ドメインサービス

    • 複数エンティティや値オブジェクトを横断するビジネスロジック。

  • ドメインイベント

    • ビジネス上重要な出来事をオブジェクト化し、システム全体へ通知する。

  • モジュール

    • 関連するドメインオブジェクトをまとめ、見通しを良くするためのパッケージ構造。

  • 集約

    • エンティティや値オブジェクトを1つのまとまりとし、ルートエンティティを介して整合性を保つ単位。

  • ファクトリ

    • 複雑な生成処理をカプセル化し、新規エンティティや集約を作る仕組み。

  • リポジトリ

    • エンティティや集約を永続化し、検索・保存・削除を提供する。

  • アプリケーション

    • ユースケースをまとめ、フロントエンドや外部システムとドメイン層を橋渡しするレイヤ。

これらの要素がDDDの戦術的設計の中心となり、ビジネスロジックをコードで表現するための強力なフレームワークを提供します。
次の章では、ソフトウェアアーキテクチャとの関連や、DDDの実践を支える設計パターンについて詳しく見ていきましょう。

6. ソフトウェアアーキテクチャとDDD

ソフトウェアアーキテクチャとは、システム全体の構造や設計方針を大枠で決めるものです。DDDを実装するうえでは、このアーキテクチャ上に「ドメインモデル」をどう配置し、どのように依存関係を管理するかが重要になります。本章では、DDDと親和性が高いソフトウェアアーキテクチャについて解説します。

6.1 ソフトウェアアーキテクチャとは?

定義

  • ソフトウェアアーキテクチャ: システムを構成する要素やそれらの相互作用、設計上の制約やガイドラインなどを決定する大枠のこと。

  • ソフトウェア開発では、機能開発や運用保守がしやすいように、「どのようにモジュールを分割するか」「どのようなパターンを採用するか」をアーキテクチャとして定義します。

3つの具体例

  1. モノリシックアーキテクチャ(単一構造)

    • すべての機能やモジュールが1つのプロジェクト内に入っている構造。

    • 小規模システムや初期段階のプロジェクトではシンプルだが、機能拡大に伴い保守が難しくなることが多い。

  2. レイヤアーキテクチャ(層構造)

    • プレゼンテーション層、アプリケーション層、ドメイン層、インフラ層など、役割ごとに層を分割する構造。

    • 後述のDIP(依存関係逆転の原則)によって、上位層が下位層に依存しすぎないように設計する。

  3. マイクロサービスアーキテクチャ

    • 機能単位をサービスとして独立させ、複数のサービスを連携させる構造。

    • 1つのサービスが独立デプロイできるため、変更や拡張がしやすい反面、サービス間通信やデータ同期などの複雑さが増す。

DDDとの関係

  • DDDはドメインモデルに焦点を当てるため、「ドメインロジック」をどの層で、どのように表現するかが重要です。

  • レイヤアーキテクチャでもヘキサゴナルアーキテクチャでも、ドメイン層を“中心”に位置付け、周辺のインフラやUIがドメインロジックに依存しないように設計するのがDDDのキーポイントとなります。

6.2 レイヤアーキテクチャとDIP

定義

  • レイヤアーキテクチャ: 典型的には以下の4層に分割されることが多いです。

    1. プレゼンテーション層(UI層)

    2. アプリケーション層

    3. ドメイン層

    4. インフラ層

  • DIP(依存関係逆転の原則, Dependency Inversion Principle):

    • 上位レイヤは、下位レイヤの実装詳細に依存しないようにする設計原則。

    • 具体的には、上位レイヤが下位レイヤにインターフェースを定義し、下位レイヤがそれを実装する形をとる。

    • これにより、ドメイン層がインフラの実装と過度に密結合になるのを防ぎ、テストや変更を容易にする。

3つの具体例

  1. プレゼンテーション層 → アプリケーション層の呼び出し

    • 画面からのリクエストやCLIからの入力をアプリケーション層に渡す際、アプリケーション層のインターフェース(サービス)を呼び出す形にする。

    • プレゼンテーション層とアプリケーション層が明確に分離されるため、UIの変更があってもビジネスロジックが影響を受けにくい。

  2. アプリケーション層 → ドメイン層の呼び出し

    • アプリケーション層がドメイン層のエンティティやドメインサービスを操作する。

    • DIPを意識して、ドメイン層のクラスに直接依存するのではなく、インターフェースを介して呼び出すこともある。

    • これにより、ドメイン層の実装詳細を外部に漏らさないようにする。

  3. ドメイン層 → インフラ層の呼び出し

    • ドメイン層のリポジトリやドメインサービスから、データベースや外部APIの呼び出し(インフラ層)を行う場合、インターフェースを介して実装する。

    • たとえば、リポジトリインターフェースはドメイン層に定義され、実際のデータアクセスロジックはインフラ層で実装する。

    • これによって、インフラの実装(DBの種類など)をドメイン層から切り離しやすくなる。

DDDとの関係

  • DDDの戦術的設計(エンティティ、値オブジェクト、リポジトリなど)が自然にレイヤアーキテクチャへ落とし込みやすい構造になっています。

  • DIPの考え方を徹底することで、ドメイン層がインフラ依存になりにくい設計を実現できます。

  • つまり、DDDで重視するドメインモデルの独立性を保ちやすいのがレイヤアーキテクチャの強みです。

6.3 ヘキサゴナルアーキテクチャ

定義

  • ヘキサゴナルアーキテクチャ(Hexagonal Architecture): 別名「ポート&アダプタアーキテクチャ」。

  • 核となるドメインロジック(アプリケーションの中心)を囲むように「ポート」と「アダプタ」を配置し、あらゆる入出力(UI、DB、外部システムなど)を“アダプタ”として差し替え可能に設計するスタイル。

3つの具体例

  1. ドメインアプリケーションとUIアダプタ

    • Web UI、CLI、モバイルアプリなど、異なるUIがあっても、共通のアプリケーションサービスを呼び出す構造。

    • UIアダプタ部分だけ実装を変えれば、同じビジネスロジックを再利用できる。

  2. ドメインアプリケーションとDBアダプタ

    • リポジトリインターフェースをドメイン側に定義し、DBアクセスの実装はアダプタとして外側に置く。

    • データベースをMySQLからPostgreSQLに変えても、アダプタを切り替えるだけでドメインロジックに影響しない設計が可能。

  3. ドメインアプリケーションと外部APIアダプタ

    • 決済サービスなどの外部APIをコールするときも、外部サービス接続アダプタを用意し、ドメイン側は統一されたインターフェースで呼び出す。

    • 後々API仕様が変更されても、アダプタ部分の修正のみで対応できる。

DDDとの関係

  • ヘキサゴナルアーキテクチャはドメインモデルをアプリケーションの中心に置き、周辺はアダプタにするという設計思想を持つため、DDDと非常に相性が良いです。

  • 戦術的設計と組み合わせることで、ドメイン層のロジックを外部要因(UI、DB、外部サービスなど)から分離しやすくなり、ビジネスロジックにフォーカスした開発・保守が可能となります。

6.4 CQRS

定義

  • CQRS(Command Query Responsibility Segregation):

    • コマンド(書き込み処理)とクエリ(読み取り処理)を明確に分けて設計・実装するパターン。

    • 書き込みモデル(Command Model)と読み取りモデル(Query Model)が異なるデータ構造を持つことも多い。

3つの具体例

  1. 読み取り専用ビューの設計

    • 例)ECサイトの「商品一覧表示」は高頻度の読み取りがあるが、在庫数や価格など書き込み発生時の負荷は相対的に低い。

    • CQRSでは、表示用のテーブルやキャッシュを最適化し、大量アクセスに耐えられる読み取り構造を用意。

    • 一方で、書き込みロジックは別のモデルで管理し、それを非同期で読み取りモデルへ反映する。

  2. イベントソーシングとの組み合わせ

    • 例)ユーザーが「注文」を行うたびに、ドメインイベントを発行し、書き込みモデルで「注文履歴」を保存。

    • 同時に読み取りモデルがそのイベントを受け取って、集計テーブルや検索用インデックスを更新する。

    • これにより、書き込み処理(Command)はビジネスロジックを厳密に管理し、読み取り処理(Query)はビューに特化した高速化が可能になる。

  3. レポーティング専用データストア

    • 例)会計システムで「月次集計」など重い集計処理が頻繁に実行される。

    • CQRSを使って、書き込み専用のコアDBと、読み取り専用のレポート用DBを分ける。

    • こうすることでコアDBが負荷を抱え込みにくくなり、システム全体のパフォーマンスが向上。

DDDとの関係

  • DDDでは「ドメインモデルにビジネスロジックを凝縮する」発想が重要ですが、単一のモデルですべての読み書き処理をまかなうと肥大化しやすいという課題があります。

  • CQRSは、読み取り/書き込みを分けてモデルを最適化するため、システムの複雑性を分割しやすくなる手法です。

  • 境界づけられたコンテキストごとにCQRSを導入し、特定のユースケースを柔軟に設計するケースもよく見られます。

6.5 イベント駆動アーキテクチャ

定義

  • イベント駆動アーキテクチャ: システム内で何らかの事象(イベント)が発生したときに、イベントメッセージを通知し、他のコンポーネントがそれを受け取り処理するスタイルのアーキテクチャ。

  • Pub/Sub(発行・購読)モデルを使った非同期通信や、複数マイクロサービス間の疎結合を実現する手段として注目されています。

3つの具体例

  1. 「注文完了」イベントの発行

    • ECサイトでユーザーが注文を確定すると、「注文完了(OrderCompleted)」イベントを発行。

    • 在庫システムや請求システムなどがそのイベントをサブスクライブし、在庫引き当てや請求書発行などを非同期に処理する。

    • こうすることで、各システムはイベントドリブンに動作し、連携が疎結合になる。

  2. 「支払い完了」イベントによる配送手続き

    • 決済サービスが支払い完了を検知すると「支払い完了(PaymentDone)」イベントを発行。

    • 配送サービスがこのイベントを受け取り、配送準備を開始する。

    • 個別のサービス同士が直接呼び出すのではなく、イベント経由でゆるやかにつながるため、変更の影響範囲を小さくできる。

  3. エラーハンドリングの集約

    • 「在庫不足」や「クレジット決済失敗」などのエラーメッセージもイベントとして発行。

    • 通知サービスやログ蓄積サービスがそれらを受け取って集計やアラートを行う。

    • 新たなエラー対応システムを導入する際も、イベントの購読先を追加するだけで済み、既存システムに手を加えなくてよい。

DDDとの関係

  • DDDの戦術的設計には**「ドメインイベント」**という概念があり、ビジネス上重要な出来事をシステム全体で共有する考え方が提案されています。

  • イベント駆動アーキテクチャは、ドメインイベントを複数のコンテキストで購読し、連携処理を非同期で実行するための手段として非常に相性が良いです。

  • システムが大規模化しても疎結合を保ちやすいため、DDDにおける境界づけられたコンテキスト同士の協調にも適しています。

6.6 まとめ

これらのアーキテクチャ手法は、DDDの「ドメインモデルを中心に据えた設計」をより強固にするためのアプローチです。プロジェクトの規模や要件に応じて、どのアーキテクチャが適切かを選択・組み合わせしながら導入すると、DDDのメリットを最大限に活かせるでしょう。

次章や今後の学習では、実際のコード例と共にこれらの要素をどう組み合わせるか、さらに詳しく掘り下げていくことになります。

7. これまでのまとめ

7.1 キーワード図

  • DDD

    • ビジネスの複雑性に正面から挑み、ドメインモデルを中心にソフトウェアを作り上げるための思想・手法

  • ユビキタス言語

    • ビジネス担当者・開発者が共通に使う言葉を定義し、設計・実装にもそのまま反映して誤解を減らす

  • 戦略的設計

    • ドメインやコンテキストの境界をどう切り分けるかを大局的に捉え、コアドメインに投資を集中する

  • 戦術的設計

    • エンティティやリポジトリなどのパターンを使い、コードレベルでドメインを表現する設計手法

7.2 DDD導入のメリットとデメリット

メリット

  1. ビジネス要件とのギャップが減る

    • ユビキタス言語により、ビジネス担当者と開発者のコミュニケーション齟齬を減らし、要件を正確に捉えやすい。

    • 機能が完成してから「思っていたものと違う」という事態を大幅に軽減できる。

  2. 複雑な要件の変更に強い

    • 戦略的設計により、ドメインを適切に分割しておくと、一部の仕様変更が全体に波及しにくい。

    • 戦術的設計でドメインモデルを明確化すると、仕様変更時にどこを修正すべきかが分かりやすい。

  3. チーム開発がスムーズになる

    • 共通言語の確立や、コンテキストの分割によって、開発者同士で「どの領域を担当するか」「何のための機能か」が明瞭になる。

    • 新規メンバーが参加しても学習コストが下がりやすく、保守運用もラクになる。

デメリット

  1. 学習コストが高い

    • DDDは用語や概念(エンティティ、値オブジェクト、ユビキタス言語、境界づけられたコンテキストなど)が多く、初学者には難しく感じる部分がある。

    • チーム全員がDDDの基本を理解するための時間やコストが必要。

  2. 設計が複雑化しやすい

    • うまく分割できない場合や、過度にレイヤ・モジュールを増やしてしまうと、かえって設計が複雑になり開発スピードが落ちる。

    • 「どの程度まで抽象化や分割を行うか」の判断には経験が必要。

  3. 小規模プロジェクトでは過剰になることがある

    • プロトタイプや簡易的なツール開発など、要件が単純な場合はDDDの効果が少ない。

    • 最初から過剰なレイヤやパターンを適用すると、かえって開発スピードを阻害するケースもある。

7.3 DDDの魅力

DDDの最大の魅力は、「ビジネスの複雑さを開発チームが真正面から捉え、言葉とモデルを共有しながら、変化に強いソフトウェアを構築できる点」にあります。

  • ビジネスの“核心”に迫る

    • DDDは「ドメインの理解」に徹底的にこだわり、単なる機能要件だけでなく「なぜそうなるのか?」を考え抜きます。

    • その結果、ソフトウェアがビジネスの競争力を高める武器となり得る。

  • コミュニケーションの質が向上する

    • ユビキタス言語を通じて、エンジニアとビジネス担当者の壁が低くなり、同じ視点で議論しやすくなる。

    • 誤解やミスコミュニケーションを防ぎ、効率的な開発に繋がる。

  • 大規模・長期プロジェクトで威力を発揮する

    • チームが拡大しても、戦略的設計でコンテキストを分割し、それぞれのチームが独立して動ける体制を整えられる。

    • 長期にわたる保守・運用でも、ドメインモデルが整理されているため技術的負債を最小限に抑えられる。

7.4 まとめ

DDDは決して「万能薬」ではありませんが、複雑なドメインを扱うプロジェクトや、競争力を左右するコアなビジネス要件を抱えたプロジェクトにおいては、非常に強力な指針となります。

  • 戦略的設計でプロジェクト全体の構造を俯瞰し、

  • 戦術的設計でコアなロジックを明確にコードに反映し、

  • ユビキタス言語を軸としてビジネス担当者と開発者の隔たりを埋めることで、
    「思った通りのシステムが出来上がった!」という喜びを分かち合えるようになります。

以上で本書の第7章(最終章)のまとめとなります。DDDの考え方と実践のポイントを押さえつつ、ぜひ読者の皆さんのプロジェクトに生かしてみてください。少しずつ取り入れるだけでも、チームのコミュニケーションや設計思想に大きな変化が生まれるはずです。

8. おわりに〜DDDとAI〜

近年、AI(人工知能)技術の進歩により、さまざまな業務領域で「AIエージェント」が活躍するようになってきました。
ここでは、まず「AIおよびAIエージェント」とは何かを定義し、次にDDD(ドメイン駆動設計)との親和性について解説していきます。

8.1 AIとAIエージェント

定義

  1. AI(人工知能)

    • AI(Artificial Intelligence)は、人間が行う知的活動(学習・推論・認識など)をコンピューターに模倣させる技術や仕組みを指します。

    • データをもとに複雑な処理を行い、パターンを学習したり推論したりすることが特徴です。

    • 機械学習やディープラーニングといった手法を用いて、高度なタスクを自動化・最適化することで、ビジネスに大きな価値をもたらします。

  2. AIエージェント

    • エージェント(Agent)は、環境からの入力(状況)を受け取り、自律的に判断し行動を起こすソフトウェアです。

    • AIエージェントは、このエージェントの判断・行動にAI技術を組み込み、高度な学習・推論を行いながら動作するエージェントを指します。

    • ユーザーとの対話や環境とのやりとりを通して、状況に応じた最適なアクションを実行したり、継続的に学習して性能を高めます。

3つの具体例

  1. チャットボット(Chatbot)エージェント

    • 例)カスタマーサポート用のチャットボット。自然言語処理(NLP)を用いてユーザーの質問や要求を理解し、適切な応答や案内を行う。

    • 学習を通じて回答の精度が向上し、問い合わせ削減や顧客満足度向上に寄与する。

  2. レコメンドエンジン(Recommendation Engine)エージェント

    • 例)ECサイトの「おすすめ商品」機能など。ユーザーの購買履歴や閲覧履歴をもとにAIが商品を推定し、適切なタイミングで提案する。

    • エージェントが顧客の行動データを学習して個々の嗜好を理解し、よりパーソナライズされた体験を提供する。

  3. 自律制御ロボット(Robotics)エージェント

    • 例)倉庫内での在庫移動や製造ラインでの組み立て作業を行うロボット。センサー情報をもとに環境を認識し、リアルタイムに最適な動きを計算して実行。

    • AIエージェントとしてのロボットは、障害物回避や作業効率の改善などを継続的に学習し、動作精度を高める。

8.2 DDDとAIエージェントの親和性

DDDとAIエージェントの関係

DDDは「ドメインの複雑さを正しくモデルに落とし込む」ための手法ですが、AIエージェントが登場すると、「AIが学習・推論する対象となるドメイン知識をいかに正しく整理できるか」が非常に重要になります。
以下では、DDDとAIエージェントの親和性を3つの具体例を挙げて説明します。

  1. 明確化されたドメイン知識をAIが利用できる

    • DDDのユビキタス言語やドメインモデルを通して、ビジネス上重要な概念やルールが整理される。

    • AIエージェントに学習データや特徴量を与える際、「何を学習対象とするか」が明確になる。

    • たとえばECサイトの在庫予測AIでは、「在庫(Inventory)」「注文(Order)」「季節要因(Seasonality)」などをドメインとして整理しておくと、データの意味付けが明確になり、AIの性能向上につながる。

  2. 境界づけられたコンテキストがAI連携をしやすくする

    • DDDの戦略的設計でコンテキストを分割することで、「どの領域のデータをAIが学習対象とするのか」や「どのエージェントと連携するのか」の範囲をはっきり分けられる。

    • たとえば「受注管理コンテキスト」「在庫管理コンテキスト」「マーケティングコンテキスト」などでAIを導入する際、それぞれの境界が明確なので、エージェントが必要なデータを収集する方法やAPI連携がスムーズに設計できる。

  3. ドメインイベントをAIのトリガーにできる

    • 戦術的設計で取り入れる「ドメインイベント」は、「注文が確定した」「支払いが完了した」「在庫が不足した」などのビジネス上重要な出来事を表す。

    • AIエージェントは、これらのドメインイベントをトリガーとしてデータを更新したり推論を走らせたり、学習を行ったりできる。

    • 例)「在庫不足イベント」が発生したら、在庫補充量をAIが予測する処理を走らせ、倉庫スタッフや発注システムに対して最適な指示を提案する仕組みなど。

    • DDDのドメインイベント設計が、イベント駆動アーキテクチャとAIを融合させやすくし、疎結合で拡張性の高いシステムを実現する。

8.3 まとめ

DDDを用いて明確に定義・分割されたドメインは、AIエージェントが高精度な学習と推論を行う基盤を提供します。

  • 整理されたビジネス概念=AI学習における正確な特徴量やカテゴリー

  • 明確化された境界づけられたコンテキスト=AIを導入する領域ごとのスムーズな連携

  • ドメインイベント=イベント駆動のAIアクションや再学習トリガー

このようにDDDとAIエージェントを組み合わせることで、ビジネスドメインとAI技術の融合を効率的かつ効果的に進めることが可能になります。

いいなと思ったら応援しよう!