ドメイン駆動設計の用語と解説(抜粋版)

More than 1 year has passed since last update.

はじめに

今度社内でDDD勉強会をやることになったので、その予習をかねて「エリック・エヴァンスのドメイン駆動設計」の末尾に載っている「用語解説」の中から、最低限これだけは覚えといた方がいいんじゃないかなっていう用語を独断と偏見で抜粋しました。
選定の基準は、

  • ドメイン駆動設計の理念に関する言葉である
  • 規模にかかわらず有用なデザインパターンである

の2点です。

また、括弧内は私の補足です。また引用部分に関しては、読みやすさを考慮して Markdown の引用用シンタックスは使っていません。

1. 概念に関する用語

ドメイン

知識、影響、または活動の領域

(問題領域、業務領域とも訳されます。ソフトウェアが表現する対象。例えば、人事評価システムであれば、従業員や業績が知識であり、目標設定や評価が活動に当たります。影響は何を指しているのか分かりません。たぶん本書にも記載がないです)

ドメインエキスパート

ソフトウェアプロジェクトのメンバで、ソフトウェア開発ではなく、アプリケーションのドメインの担当者。ソフトウェアの単なるユーザと違い、ドメインエキスパートはその対象に関する深い知識を持っている。

(「プロジェクトのメンバ」というのがミソ)

ユビキタス言語

ドメインモデルを取り巻いて構築され、チームのあらゆる活動をソフトウェアと結びつけるために、チームメンバ全員によって使用される言語。

(用語集をかっこ良く言い替えただけのような気もしますが、エンジニアも非エンジニアも同じ言葉を使いましょう、というお約束)

モデル駆動設計

ソフトウェア要素のサブセットがモデル要素と密接に対応している設計。また、相互に一致した状態を保ちながら、モデルと業務を共に開発するプロセス。

(何を言ってるのか分からねーと思うが、設計フェーズでモデリングをするのはよくある話だけど、実装に入ったら、モデル図は捨てられたり忘れられたりすることがある。モデル駆動設計では、モデルは常にコードに「密接に」対応していて、実装時に設計の変更が必要と感じたら、モデルを変えるところから始めましょうっていう考え方、なんだと思います)

イテレーション

プログラムが小さいステップで繰り返し改善されるプロセス。または、そのステップの1つ。

(iteration = 反復。要件定義 → 設計 → 実装 → テスト → リリースの工程を2週間程度の短いスパンで繰り返す開発手法において、各サイクルのことをイテレーションと呼びます。まぁ、これは DDD の用語じゃないですね)

コンテキスト

単語や文が出てくる環境で、その単語や文の意味を決定するもの。

(日本語では文脈。よくある話だと、ユーザーという概念は、コンテキストによって違うって話で、ウェブサービスのユーザーは現在ログインしているユーザーを指して、そのサービスの管理システム側から見たユーザーは、登録してるユーザーを指す、みたいな。これも別に DDD の用語じゃないですが、コンテキストをソフトウェアでどう表現するかっていうのは DDD のキモのひとつかも)

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

特定モデルを適用できる限定された範囲。コンテキスト境界を定めることで、チームメンバは何を一致させるべきで、何を独立して開発できるのかについての理解を明確化し、共有できる。

(コンビニと客の関係で言うと、客はバックヤードには入れないわけで、売り場やレジが客がコンビニに対して持つ知識や活動のコンテキストになります。バックヤードは店員や配送員のコンテキストですね。境界づけられたコンテキストをソフトウェア的に表現したのがサブシステムだったりパッケージやモジュールになろうかと思いますが、言語によってはあまり制約のない境界になってしまうので、そういう言語を使うときはちょっと工夫すべきかもしれません。最近流行りの Microservices とかだとこれがはっきり境界づけられるわけですけども)

コンテキストマップ

プロジェクトに含まれる複数の境界づけられたコンテキストやコンテキストとモデルとの実際の関係を表現したもの。

(境界づけられたコンテキストは多くの場合複数の異なるチームまたはメンバーが担当することになると思います。例えば、他のシステムとのデータの受け渡しがある場合、異なるコンテキストで異なるチームがつくったモデルは当然異なるものになるわけで、その違いを吸収するための変換マップが必要ですよねって話です。単純な例で言うと、同じユーザーが、あるコンテキストでは応募者であり、別のコンテキストでは当選者である、みたいなケースで、応募サービス、抽選サービス、当選通知サービスといった境界づけられたコンテキストがあり、それぞれ別のモデルを持っている場合、その間に変換するためのサービスが必要で、変換ロジックは双方のチームで共有しましょうね、ということです)

2. 設計に関する用語

デザインパターン

一般的な設計上の問題を特定のコンテキストにおいて解決するためにカスタマイズされた、オブジェクトとクラスのコミュニケーションに関する記述。

(もはや古典ともなった「デザインパターン」という本がオブジェクト指向設計・プログラミング界に与えた影響はものすごくて、もはやデザインパターンなくしてオブジェクト指向プログラミングは語れないくらいな位置づけになっています。DDD本もデザインパターン集の側面があるし、パターンの発見・活用がオブジェクト指向開発のキモになってますね)

レイヤー化アーキテクチャ

ソフトウェアシステムの関心事を切り離し、ドメイン層を他から分離するテクニック。

(「ドメイン層を」ってところがポイントで、上から順に、ユーザーインタフェース層、アプリケーション層、ドメイン層、インフラストラクチャ層に分割し、それぞれに適切なクラス配置をすることで、ドメイン層にはドメインに関連するクラスのみを配置できます。ドメイン層のクラスには後述する「エンティティ」「値オブジェクト」「サービス」の3つのデザインパターンを適用します。処理の呼び出しは上の階層のオブジェクトから行われ、下の層のクラスから上の層のクラスへの命令は行わない)

エンティティ

本質的に、属性によってではなく、連続性と同一性の連なりによって定義されるオブジェクト

(何を言ってるのか分からねーと思うが、ここで使われている連続性という言葉は、数学の用語みたいで、ググったけどよく分からなかったのでだれか教えてください。教わっても分からないと思うのでやっぱいいです。まぁ、同一性だけ理解していれば事足りると思うので、ここだけ補足すると、氏名を属性として持つ「人」オブジェクトがあったとして、たとえ同姓同名であっても同一人物として扱ったらダメですよね、という類のクラスです。逆に、住所を属性として持つ「会社」オブジェクトがあったとして、その会社が移転をして別の住所になったからといって別の会社として扱ったらダメですよね、ということも言えます)

値オブジェクト

ある特徴や属性を記述するが、同一性の概念を持たないオブジェクト。

(値オブジェクトはイミュータブルにするのが定石。難しいのは、エンティティにするか値オブジェクトにするかはコンテキストによって決めなければならない、という点で、同一性がそのコンテキストにおいて意味を持つケースと持たないケースがある、ということ。例を引用すると、「通販会社のソフトウェアでは、クレジットカードを確認し、小包の宛先とするのに住所が必要である。しかし、同居人も同じ会社に注文したとしても、両者が同じ場所に住んでいると気づくことは重要ではない。この場合、住所は値オブジェクトである。(中略) 電力会社のソフトウェアにおいては、住所は、電線と電力サービスの目的地に相当する。同居人それぞれが電力サービスを依頼する電話をかけた場合、会社は同じ場所であることに気がつく必要がある。ここでも、住所はエンティティである。」)

サービス

インタフェースとして提供される操作。このインタフェースはモデル内で独立していて、カプセル化された状態を持たない。

(基本的にビジネスロジックはエンティティのオブジェクトが担当するものですが、ドメイン層にあるどのクラスにも相応しくない振る舞いというのがあったとき、その振る舞いを責務として持つサービスクラスを作るようにします。DDD本では、サービスは各レイヤーに配置可能で、ドメイン層にあるサービスはドメインサービス、アプリケーション層にあるサービスはアプリケーションサービス、と呼んでいます。例として、資金振替アプリケーションサービスは、入力(XMLリクエストなど)を理解する、処理を実行するよう、ドメインサービスにメッセージを送信する、確認を待つ、インフラストラクチャサービスを利用して、通知を送信することを決定する、という処理を行い、資金振替ドメインサービスは、必要な口座オブジェクトおよび元帳オブジェクトとやり取りし、適切な引き落としと振り込みを行う、結果(振替の可否など)の証跡を出力する、という処理を行います)

集約

関連したオブジェクトの集合で、データを変更する目的で1つの単位として扱われるもの。外部から参照できるのは、集約でルートに指定されたメンバ1つに限られる。一貫性に関するルールの集合が、集約の境界内に適用される。

(注文とそれに紐づく注文明細があった場合、ルートオブジェクトは注文になります。集約されたオブジェクトのライフサイクルはルートオブジェクトが基準となり、単独で存在することはありません)

仕様

(なぜかこのパターンは用語解説には載っていませんが、有用だと思うので載せます。定義を本文から引用すると、「特殊な目的を持った述語的な値オブジェクトを明示的に作成すること。仕様とは、あるオブジェクトが何らかの基準を満たしているかどうかを判定する述語である。」例として、顧客と請求書というドメインクラスがあったとき、その請求書が遅延しているかどうか、という条件が複雑な場合、それらの条件を仕様クラスとして抽出し、条件を満たしているかどうかの判定をそのオブジェクトにさせることで、複雑な条件判定を隠蔽しようというパターンです)

ファクトリ

クライアントのために複雑な生成ロジックをカプセル化し、生成されたオブジェクトの型を抽象化するための仕組み。

(あるオブジェクトが、パラメーターによって異なるクラスのインスタンスになったり、異なる属性を持ったりするような、生成に複雑なルールがある場合、ファクトリクラスでその複雑性を引き取ります)

リポジトリ

格納、取り出し、および検索のふるまいをカプセル化し、オブジェクトのコレクションをエミュレートする仕組み。

(なんで「ふるまい」が平仮名なのかが気になりますが、要するにデータベースとのインタフェースになるクラスのことです。データベースに永続化されたデータは、ORマッパーがあれば、オブジェクトのコレクションになり、なければ単純なハッシュテーブルの配列として表現されます)

まとめ

  • ドメイン駆動設計とは、ビジネスや問題解決の対象をドメインと呼び、ドメインをモデル化することを最も重要とするモデル駆動設計のひとつである
  • ドメインの物事を表現するために「ユビキタス言語」と呼ばれる共通の言葉を使わなければならない
  • モデルとコードは対になっていなければならない
  • 複雑なドメインを扱う際には、コンテキストごとにシステムやモジュールを分割し、コンテキストをまたがる処理を行う場合には変換テーブルを用いて、お互いの変更が相手に影響を与えないように独立させておかなければならない
  • ドメインを扱うクラスを限定するために、クラスの役割ごとにレイヤー (ユーザーインタフェース層、アプリケーション層、ドメイン層、インフラストラクチャ層) を分ける「レイヤー化アーキテクチャ」を採用する
  • レイヤー化アーキテクチャにおいては、常に上の層のクラスから下の層のクラスへ命令の呼び出しが行われる
  • ドメイン層のクラスは、エンティティ、値オブジェクト、サービスのいずれかである
  • アプリケーション層やインフラストラクチャ層にもデザインパターンを適用し、複雑さを局所化しておくことが望ましい

間違いがあればコメント欄にてご指摘ください。

参考

[ 技術講座 ] Domain-Driven Designのエッセンス -目次-|オブジェクトの広場

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.