コンテンツにスキップ

ドメイン駆動設計(DDD)入門|ドメイン・ドメインオブジェクト・ドメインモデルの違い

ドメイン駆動設計(Domain-Driven Design、略して DDD) を学び始めると、まず「ドメイン」「ドメインオブジェクト」「ドメインモデル」という似た言葉が並び、どこから手をつければよいか迷いがちです。

この記事は、DDD 入門として次の 4 つを、ネットショップや SNS の具体例つきでやさしく整理します。

ドメインとは、「このアプリは何のためのものか」を表す言葉です。

プログラムの中の変数名やクラス名の話ではありません。
アプリが再現しようとしている、現実の仕事や出来事のことを指します。

ネットショップのアプリを作るとき、ドメインは「ネットで商品を売る・買うこと」です。

お客さんが実際にアプリを使う流れを想像してみましょう。

flowchart LR
    A[商品を選ぶ] --> B[カートに入れる]
    B --> C[注文する]
    C --> D[支払う]
    D --> E[自宅に届く]

この一連の流れ全体が、ネットショップのドメインです。
「画面を表示する」「ボタンを押す」といったプログラムの操作ではなく、お店として実際に起きていることの方を指します。

ドメインの中には、いろいろな「要素」と「ルール」がある

Section titled “ドメインの中には、いろいろな「要素」と「ルール」がある”

ドメインの中には、ユーザー、商品、注文、支払い、配送など、いろいろな要素が含まれます。
それぞれには データルール があります。

データの例ルールの例
商品名、価格、在庫数、説明文価格は 0 円以下にできない
在庫が 0 の商品は購入できない
データの例ルールの例
注文日時、購入した商品、数量、合計金額在庫が足りなければ注文できない
発送済みの注文はキャンセルできない
データの例ルールの例
名前、電話番号、メールアドレス、住所電話番号かメールアドレスの、どちらか一方は必須
メールアドレスは @ を含む形式であること
データの例ルールの例
支払い方法、支払い金額、支払い日時注文金額と同じ金額だけ支払える
すでに支払い済みの注文に、もう一度支払いできない

この 「データ」と「ルール」のセット が、ドメインを構成する中身です。
「商品名は何か」だけでなく、「在庫がなければ売れない」という 現実のお店の決まりごと もドメインの一部です。

SNS アプリなら、ドメインは「SNS で人とつながること」です。

要素データの例ルールの例
投稿本文、画像、投稿日時、投稿者本文も画像も両方空の投稿はできない
フォローフォローする人、フォローされる人自分自身をフォローできない
ユーザー表示名、プロフィール画像表示名は空にできない

ネットショップと SNS ではドメインが違うので、必要なデータもルールも変わります。
配達アプリなら「配達員」「配達先」「配達時間」、予約アプリなら「予約枠」「キャンセル期限」といった要素が中心になります。

ここまでが、プログラムとは切り離した ドメイン の話です。
次の節から、これをプログラムに落とし込んでいきます。

ドメインオブジェクト(Domain Object)とは、ドメインの要素をプログラムで表現したオブジェクトです。

意味
ドメイン現実の世界の話(お店のルール、注文の流れなど)
ドメインオブジェクトドメインの要素を、プログラムの部品として表現したもの(UserOrder など)

ドメインの中にある「ユーザー」「商品」「注文」といった要素を、ひとつひとつプログラムに書き起こしたものがドメインオブジェクトです。

ドメインの要素をプログラムに落とし込む

Section titled “ドメインの要素をプログラムに落とし込む”

ここまで見てきたドメインの要素を、プログラムに落とし込むイメージは次のとおりです。

flowchart LR
    subgraph domain["ドメイン(現実のお店の世界)"]
        D1["商品 … 名前・価格・在庫、売れない条件"]
        D2["注文 … いつ・何を・いくつ買ったか"]
        D3["ユーザー … 連絡先、登録の決まり"]
    end
    subgraph objects["ドメインオブジェクト"]
        O1[Product]
        O2[Order]
        O3[User]
    end
    D1 --> O1
    D2 --> O2
    D3 --> O3

ドメインの要素(商品、注文、ユーザーなど)ごとに、データとルールをひとまとめにしてプログラムに書いていきます。
そのひとまとめが、ドメインオブジェクトです。

ドメインオブジェクト表している現実のもの持っているルールの例
Userお客さん連絡先はどちらか一方が必須
Product商品価格は 0 円以下にできない
Order注文在庫が足りなければ注文できない
Payment支払い注文金額と同じ金額だけ支払える

ドメインオブジェクトには、次の 2 つがセットで入っています。

  1. データ … 名前、価格、在庫数など、その要素が持っている情報
  2. 振る舞い … ルールを守るための操作やチェック(「注文する」「キャンセルする」など)

データだけを入れておく箱(データベースの行のようなもの)ではなく、ルールも一緒に持ったプログラムの部品 であることがポイントです。

具体例:User ドメインオブジェクト

Section titled “具体例:User ドメインオブジェクト”

ユーザーに関するドメインオブジェクトは、だいたい次のような形になります。

classDiagram
    class User {
        名前
        電話番号
        メールアドレス
        住所
        連絡先チェック()
        メール形式チェック()
    }

プログラムで書くと、次のようなイメージです(言語は問いません)。

class User(
val name: String,
val phoneNumber: String?,
val email: String?,
) {
init {
// ルール:連絡先はどちらか一方が必須
if (phoneNumber.isNullOrBlank() && email.isNullOrBlank()) {
throw Error("電話番号かメールアドレスのどちらかが必要です")
}
// ルール:メールアドレスの形式チェック
if (!email.isNullOrBlank() && !email.contains("@")) {
throw Error("メールアドレスの形式が正しくありません")
}
}
}

ここでは データ(名前・電話番号など)ルール(保存できる条件) が、ひとつの User にまとまっています。

具体例:Order ドメインオブジェクト

Section titled “具体例:Order ドメインオブジェクト”

注文も同じ考え方です。

classDiagram
    class Order {
        注文日時
        購入した商品
        数量
        合計金額
        発送済みかどうか
        注文する()
        キャンセルする()
    }

「注文する」「キャンセルする」といった操作も、ドメインオブジェクトの中に書きます。
そうすると 「注文はこういう条件のときだけできる」 というルールが、Order の中だけで完結します。

データベースのテーブル設計と混同しやすいですが、役割は少し違います。

役割
データベースのテーブルデータを 保存する ための形(名前、電話番号などをどの列に入れるか)
ドメインオブジェクトデータに加えて、ルールや操作 も含めたプログラム上の表現

たとえば users テーブルに「電話番号」「メールアドレス」の列があっても、
「どちらか一方は必須」というルールはテーブルだけでは表現できません。
そのルールをプログラムで書いたものが、User ドメインオブジェクトです。

ドメインオブジェクトを使うとどうなるか

Section titled “ドメインオブジェクトを使うとどうなるか”

API や画面の処理は、ドメインオブジェクトを 使う側 になります。

flowchart LR
    subgraph api["画面・API"]
        A1[ユーザー登録 API]
        A2[メール変更 API]
        A3[注文 API]
    end
    subgraph dom["ドメインオブジェクト"]
        U["User(ルール付き)"]
        O["Order(ルール付き)"]
    end
    A1 -->|使う| U
    A2 -->|使う| U
    A3 -->|使う| O

API ごとに同じチェックを書く必要がなく、ドメインオブジェクトに任せるだけ でルールが守られます。

個々の部品がわかったら、次はそれらを組み合わせた 全体の設計 を見ていきます。

ドメインモデル(Domain Model)とは、ドメインオブジェクトを組み合わせて作った、全体の設計です。

意味
ドメインオブジェクトドメインの要素を表す、個々のプログラムの部品
ドメインモデルそれらを組み合わせた 全体像(何があり、どう関係するか)

レゴブロックに例えると、ドメインオブジェクトがひとつひとつのブロックで、ドメインモデルがそれらを組み立てた完成品の設計図です。

具体例:ネットショップのドメインモデル

Section titled “具体例:ネットショップのドメインモデル”

ネットショップのドメインモデルは、だいたい次のような全体像になります。

classDiagram
    class User {
        お客さんの情報と登録ルール
    }
    class Product {
        商品の情報と販売ルール
    }
    class Order {
        注文の情報と注文・キャンセルのルール
    }
    class Payment {
        支払いの情報と支払いルール
    }
    User "1" --> "*" Order : 注文する
    Order "*" --> "*" Product : 含む
    Order "1" --> "1" Payment : 支払われる

ドメインモデルが示すのは、要素の一覧 だけでなく、要素同士の関係 です。
「注文には必ずユーザーが紐づく」「支払いは注文に対して行われる」といったつながりも、ドメインモデルの一部です。

ドメインモデルを整理するときは、次の 2 点をはっきりさせます。

  1. 何があるか … ユーザー、商品、注文など、ドメインに登場する要素(=ドメインオブジェクト)
  2. どう関係するか … 注文はユーザーを持つ、支払いは注文に対して行われる、など

ここまで出てきた 3 つの言葉を、まとめて整理します。

flowchart LR
  D["ドメイン<br/>ネットショップという仕事"]
  DM["ドメインモデル<br/>全体の設計・構造"]
  DO["ドメインオブジェクト<br/>User, Product, Order …"]
  D -->|現実をプログラムで表現| DM
  DO -->|個々の部品を組み合わせる| DM
  • ドメイン … 現実の仕事やルール
  • ドメインオブジェクト … ドメインの要素を表す、個々のプログラムの部品
  • ドメインモデル … ドメインオブジェクトを組み合わせた全体の設計

ドメインオブジェクトをきちんと作り、それらの関係をドメインモデルとして整理しておくと、プログラム全体の見通しがよくなります。
次の節で説明するドメイン駆動設計は、まさにこの 「ドメインオブジェクトを中心にプログラムを組み立てる」 考え方です。

ドメイン駆動設計(Domain-Driven Design、DDD)とは、アプリのルールを大切にして、プログラム全体をそのルールが守りやすい形に組み立てていく設計のやり方です。

難しく聞こえるかもしれませんが、やっていること自体はシンプルです。

ルールは一か所に書いておき、他の場所ではそれを使うだけにしよう

これがドメイン駆動設計の基本的な考え方です。

ユーザー情報について、次のルールがあるとします。

「電話番号かメールアドレスの、どちらか一方は必ず登録されていること」

ルールをあちこちに書いてしまう場合

Section titled “ルールをあちこちに書いてしまう場合”

ユーザーに関するルールを、API ごとにバラバラに書いてしまうと、こうなります。

flowchart TB
    subgraph scattered["ルールをあちこちに書く"]
        API1[ユーザー登録 API] --> C1[連絡先チェック]
        API2[メール変更 API] --> C2[連絡先チェック]
        API3[電話番号変更 API] --> C3[連絡先チェック]
    end

同じチェック処理が何度も必要になり、書き忘れが起きやすくなります。
「メールアドレス変更 API だけチェックを入れ忘れた」といったバグの原因になります。

ルールを一か所にまとめる場合

Section titled “ルールを一か所にまとめる場合”

ユーザーに関するデータとルールを、User ドメインオブジェクト として用意しておきます。
そこに値を入れようとしたとき、ルール違反ならエラーを出すようにします。

flowchart TB
    subgraph centralized["ルールを一か所にまとめる"]
        API1[ユーザー登録 API] --> User
        API2[メール変更 API] --> User
        API3[電話番号変更 API] --> User
        User["User ドメインオブジェクト<br/>連絡先ルールを一か所で管理"]
    end

たとえば、電話番号もメールアドレスも空の状態で保存しようとすると、「どちらかは必要です」というエラーが出る、といった動きです。

こうしておけば、ユーザー登録でも変更でも、同じルールが必ず使われる ようになります。
チェック処理の書き忘れを防げます。

このやり方では、ドメインオブジェクト(アプリのルールを表現したプログラムの部品)を中心に組み立てる ことになります。

画面の見た目や API の形より先に、「このアプリでは何が正しいのか」というルールをドメインオブジェクトとしてはっきりさせ、それをプログラムの土台にします。
だから「ドメインを軸にした設計」、つまりドメイン駆動設計と呼ばれます。

DDD(ドメイン駆動設計)とは何ですか?

Section titled “DDD(ドメイン駆動設計)とは何ですか?”

DDD は、アプリのビジネスルール(ドメイン)をプログラムの中心に置き、同じルールをあちこちに書かず、ドメインオブジェクトに一か所でまとめる設計の考え方です。詳しくは ドメイン駆動設計とは を参照してください。

ドメインとドメインオブジェクトの違いは?

Section titled “ドメインとドメインオブジェクトの違いは?”

ドメインは現実世界の話(お店の注文ルールや在庫の決まりごと)で、ドメインオブジェクトはそれを UserOrder のようなプログラムの部品として表現したものです。詳しくは ドメインとはドメインオブジェクトとは を参照してください。

ドメインモデルとドメインオブジェクトの違いは?

Section titled “ドメインモデルとドメインオブジェクトの違いは?”

ドメインオブジェクトは個々の部品、ドメインモデルはそれらの関係を含めた全体の設計図です。レゴでいえば、ブロックがドメインオブジェクト、組み立てた完成品の設計がドメインモデルです。

ドメインオブジェクトとデータベースのテーブル設計の違いは?

Section titled “ドメインオブジェクトとデータベースのテーブル設計の違いは?”

テーブル設計はデータの保存形式を決めるもので、ドメインオブジェクトはデータに加えてルールや操作も持つプログラム上の表現です。「どちらか一方は必須」といったルールは、テーブルだけでは表現しきれません。

ドメイン駆動設計は初心者でも取り入れられますか?

Section titled “ドメイン駆動設計は初心者でも取り入れられますか?”

はい。最初の一歩として覚えておきたいのは、「同じルールをあちこちに書かない。一か所にまとめて使い回す」 という点だけです。大規模な DDD の手法をすべて一度に導入する必要はありません。

  • ドメイン … アプリが再現しようとしている現実の仕事・出来事。商品・注文・ユーザーなどの要素と、そのデータ・ルールの集まり
  • ドメインオブジェクト … ドメインの要素を表す個々の部品(UserOrder など)。データと振る舞い(ルール)をセットで持つ
  • ドメインモデル … ドメインオブジェクトを組み合わせた全体の設計。要素同士の関係や構造を表す
  • ドメイン駆動設計 … ドメインオブジェクトを中心に、プログラム全体をそのルールが守りやすい形に組み立てる設計のやり方

最初の一歩として覚えておきたいのは、「同じルールをあちこちに書かない。一か所にまとめて使い回す」 という点です。
これだけでも、プログラムは読みやすくなり、バグも減りやすくなります。