☝️

DDD入門:用語解説・モデリング編

2023/12/13に公開

はじめに

こんにちは、クラウドエース Backend Division 所属の秋庭です。
私は今年新卒で入社したのですが、入った案件では DDD (ドメイン駆動設計)を元に設計・実装が行われていたため DDD について学ぶ必要がありました。
この記事は、私が何も知らない状態から DDD について学び始めた一歩目のアウトプットであり、同様にこれから一歩を踏み出す方の手助けになるものとなったら嬉しいです。

この DDD 入門記事は大きく「用語解説・モデリング編」と「コーディング編」に分かれており、用語の紹介と実際の作業を通して DDD の概観を紹介できればと思います。
この記事は「用語解説・モデリング編」となります。この記事の続きとなる「コーディング編」もぜひご覧ください!
https://zenn.dev/cloud_ace/articles/bdbd4c1423e221

対象

想定読者

DDD の用語や概念、モデリングについてこれから学ぼうと考えている方。

記事内容

この記事では主に DDD における用語を説明し、理解を深めることを目標としています。
この記事で行うのは用語の解説とモデリングの作業例となっており、コーディング部分は続く別記事をご覧下さい。
また、DDD から発展するマイクロサービス アーキテクチャや DB であったり、システム設計・開発におけるマクロな話は行いません。

DDDについて

DDD における具体的な作業に入る前に、DDD とは何かについてから入っていきたいと思います。
DDD とは、一言でまとめると ビジネスとソフトウェアを密接に結びつける設計 と言えるのではないかと思います。
DDD に触れ始めて似たような言葉を最初目にした時、それは普通のことなのでは・・?と感じました。( DDD はもっと難解な言葉で複雑なプロセスを表しているものだと思っていました。)
DDD はそんな当たり前と感じる事を丁寧に行うテクニックが含まれており、DDD 用語といった形でそれらが体系立てられています。
それらのテクニックを使って複雑なビジネスを理解し、ソフトウェアに反映させます。
これが DDD の ビジネスとソフトウェアを密接に結びつける設計 の詳細です。
ビジネスサイドの変更がコードに伝わりやすく、コードで見つかった洞察もビジネスへと還元しやすい状態を目指しています。DDD は、モデリングとコーディングの反復を通じてそのような状態を実現しようとしています。

用語一覧

DDD で基本となる用語を以下にまとめました。
また、用語と作業の位置関係を分かりやすくするために図を作成しました。位置関係の把握の参考になればと思います。

DDD内での用語の位置関係

ドメイン

ドメインとは、課題を解決したいビジネスの範囲 のことです。銀行システムであれば「預金」や「送金」、EC サイトであれば「ユーザ管理」「商品管理」「注文」「配送」といった概念が含まれています。

ドメインエキスパート

ドメインに関する知識が豊富にあり、ビジネスの要件や制約に詳しい人物のことを指します。

ユビキタス言語

開発チームとドメインエキスパートがコミュニケーションを取るために使用する 共通の言語 です。
会話や資料、コード上においてドメインエキスパートと開発者が同じ言葉を使用し、認識の齟齬を防ぎます。

ユビキタス言語がない時🤔 ユビキタス言語がある時👍
ユビキタス言語がない時 ユビキタス言語がある時

ドメインモデル

ドメインモデルは、ドメインの事象や概念が抽象化されたモデル です。
ドメインモデルを図に起こしたドメインモデル図を作成することが、この記事のモデリング作業でのゴールです。
ドメインモデリングを行う際にはドメインエキスパートとの密な対話が必須です。これにより、ビジネスの知識をソフトウェアの設計に反映することができます。
ドメインモデル図上で使用される言葉はユビキタス言語です。

エンティティ

エンティティは、識別子によって比較されるオブジェクトです。
エンティティは、作成・更新・削除といったライフサイクルを持ちます。
エンティティはシンプルに人間をイメージするのが良いと思います。体重が増減しても、服を着替えてもその人はその人のままです。
逆に、構成要素が同一であっても識別子が異なればそれは異なるオブジェクトです。これは人間で言う同姓同名と例えることができます。

値オブジェクト

値オブジェクトは、値によって比較され、不変であるオブジェクトのことを指します。
フィールドや構造体、エンティティの識別子も値オブジェクトです。比較する際に見るのは全て値です。
実装においては、値オブジェクトは不変であるため違う値を作成する場合には再生成が必要となります。それにより常に制約のチェックを挟み、値に対し制約を強制します。

制約

制約(ビジネスルール)は、オブジェクトを作成する際に 守らなくてはいけないルール です。
例えば、「ユーザ名は8文字以上である」や「同時に購入できる個数は10個以下」などビジネスの上には沢山の制約が存在しています。
値オブジェクトの不変であるという性質と組み合わせ、システムの中で一貫した制約を強制します。
社内では、制約を通常の制約と振る舞いの制約という2つに分けています。振る舞いの制約は、後述のドメインサービスの項で説明しています。

集約

集約は、ドメインオブジェクト同士で強く整合性を取りたい範囲 を表します。また、永続化と再構築を行う単位でもあります。
強い整合性とは、オブジェクトの状態によって別のオブジェクトが影響を受ける場合などです。例えば、EC サイトでカート内の商品の点数に応じて割引率が変わるなどでしょうか。割引率と商品の数は連動しており、この場合常に両方を同時に更新する必要性があります。
集約内で親となるエンティティ(集約ルート)が存在し、集約内のオブジェクトの整合性を保つため集約への操作は全て集約ルートを経由して行います。
集約の範囲を決めることは機械的に出来ず難しいです。また、DB のロックとも関連するため大きすぎず小さすぎず守るべき強い整合性を探るのが重要です。

ドメインオブジェクト

ドメインオブジェクトは、ドメインモデルをソフトウェア上で表現したものです。
エンティティや値オブジェクト、後述するドメインサービスやリポジトリなどが含まれています。
これらを使用し、ドメインモデルをソフトウェアとして動くものにします。

ドメインサービス

ドメインサービスは、ドメインの制約の中でも振る舞いに関する制約の表現を行います。
はじめに、通常の制約と振る舞いの制約の違いについて説明します。
振る舞いの制約の一例として挙げられるのは「重複の確認」です。"重複していない"という制約はそれ以外の制約とは性質が異なります。
「ユーザネームが4文字以上である」という通常の制約は、その場で判断することができます。しかし、重複していないという制約の確認はどうでしょうか?通常の制約とは違い、その場で判断するのには無理があります。

// ユーザネームは4文字以上でなくてはならない
// その場で判断することが出来る
userName := "hoge"
if len(userName) < 4 {
    return nil, errors.New("ユーザネームは4文字以上で入力して下さい。")
}
user := User{userName}

// ユーザネームはユーザ内で重複していない値である
// 作成したオブジェクト自身で制約を判断しようとすると違和感がある
userName := "hoge"
user := User{userName}
isExist := user.UserNameExists(user)

このような、フィールドを作る際にその場でオブジェクト自身が判断することが難しいような制約を社内では振る舞いの制約と呼び、通常の制約と区別しています。
通常の制約と振る舞いの制約の実装上の違いは、続く「コーディング編」の記事を見ていただくと分かりやすいと思います。

リポジトリ

リポジトリは、集約単位でのオブジェクトの永続化と再構築を提供します。
実装の中で、リポジトリはインターフェイスとして定義され具体的な実装(DB に対する操作など)とは切り離されます。
これにより、制約などドメインの知識を表現する上で特定の技術に依存することがなくなります。
この、使う側と実装の双方を抽象に依存させるテクニックを 依存性逆転の原則 と言い、DDD において重要な役割を果たすテクニックです。

モデリングとコーディングの関係

モデリングとコーディングの作業に入る前に、2つの作業の関係について説明します。
モデリングとコーディングを通して達成したいことは、「ソフトウェアに反映させやすい」モデルの作成と、モデルを元にコーディングを行ったソフトウェアで「問題解決」が行えることです。
最初から完成されたモデルを作成することは難しいので、モデリングとコーディングのイテレーションを通して相互にブラッシュアップを行っていきます。
イテレーション
モデリングとコーディングは一度きりの独立した作業ではなく、並行した関係性です。

🙅‍♀
🙆‍♀ イテレーションを通して相互にブラッシュアップ

モデリング

この記事では、一般的なユーザ登録のユースケースを例に、モデリング及びコーディングを行っていきます。
ユーザ登録は、仮登録画面にメールアドレスを入力すると確認コードがパラメータとして付与された本登録画面へのリンクが届き、そこから各種ユーザ情報を入力することで本登録が完了するフローを想定しています。

モデリングはコーディングを行う前の段階で、ここではドメインの分析を行います。
ドメインモデルは、ドメインにある概念を抽出して作成したモデルです。
ビジネス要件や、ドメインについて詳しいドメインエキスパートと共にドメイン内にある概念や制約を図に起こします。

ドメイン内には以下のようにフィールドや、制約(ビジネスルール)など多くの情報が含まれています。

ユーザ登録のドメインに含まれる情報
ドメインに含まれる色々な情報

ドメインに含まれる情報から、以下のドメインモデル図を作成しました。

ドメインモデル図
作成したドメインモデル図

各フィールドの補足

ドメインモデル図内の各要素について解説していきます。

制約はフィールドごとにまとめ、ドメインモデル図上に記載しています。
制約は通常の制約と振る舞いの制約の2種類に分け、それぞれ対象のオブジェクトと結びついています。

エンティティの識別子として、ユーザエンティティはユーザ UUID を、仮登録情報エンティティは仮登録情報 UUID を持っています。エンティティはこれらの識別子を元に同一であるか判断されます。

値オブジェクトは、ユーザ属性はユーザが入力するもの、ステータスはシステムが変更するもの、と性質で大きく分けました。
例えば、ユーザの情報を更新するユースケースがあった場合、メールアドレスのみが変更されてもユーザ属性というまとまりごと入れ替えます。

集約は、ユーザ集約と仮登録集約に分けています。
集約は用語説明の中で紹介した通り、永続化・再構築を行う単位です。仮登録情報がユーザ集約に含まれる場合、ユーザ属性の更新など登録以外のユースケースでは使用しない仮登録情報も取得することになってしまいます。それは無駄なリソースとなってしまうと思い、ここでは集約を分けました。

「ユーザ」や「ユーザ属性」などドメインモデル図上に書かれた単語がユビキタス言語です。
会話の中ではユーザを使用し、コード上ではそれを User と表現することで齟齬を防ぎます。

ユーザ登録の画面を想像すると、多くの場合メールアドレスやパスワードの再入力の項目があります。
しかし、ドメインの概念を表す上で再入力の項目をドメインモデル図上でも表す必要は無いと考え省いています。
再入力がビジネス上重要であるのならば、それは制約でありドメインモデル図上に表現される必要があると思います。そういったことを判断するためにも、ドメインエキスパートからのヒアリングなどを通して開発者がビジネスサイドの理解を深めることが重要となります。

このドメインモデル図はあくまで私が考えた一例であり、作る人や要件によって全く違うものになると思います。
DDD において重要なのはイテレーションを通したモデルの改善を行うことなので、用語と実作業の対応を見て流れを理解して頂けたらと思います。

ドメインモデル図以外にも、ユースケース図 や具体的な値の例を記載した オブジェクト図 などがあるとより認識の相違を防ぐことができます。

ドメインモデル図(抜粋) オブジェクト図(抜粋)
ドメインモデル図 オブジェクト図

おわりに

この記事では、DDD 用語の紹介とモデリング作業を行いました。
ドメインモデル図を作成したので、続く「コーディング編」ではソフトウェアに落とし込む作業を行っていきたいと思います。
「用語解説・モデリング編」をここまで読んで頂き、ありがとうございます!

Discussion