開発支援・コーディング

ドメイン駆動設計とは|「顧客」を1つのモデルにまとめたら、両方の部門が登録できなくなった

ドメイン駆動設計は結局なにをする手法なのかモデルを1つにまとめると何が困るのか小さいアプリでも取り入れる意味はあるのか

ドメイン駆動設計(DDD)と聞くと、難しい用語が並ぶ設計手法という印象があると思います。ただ、出発点は素朴です。業務で使っている言葉を、そのままコードの名前にする。これだけです。

とはいえ抽象的な話だけでは伝わらないので、今回も動かしました。「顧客」という同じ言葉を1つのモデルにまとめたコードを書いて、販売部門と配送部門の両方から登録してみます。結果を先に言うと、どちらの部門も登録できませんでした

この記事の要点

  • DDDの出発点は業務の言葉をそのままコードの名前にすること
  • 同じ「顧客」でも部門ごとに意味が違う。無理に1つにすると必須項目が矛盾する
  • 実測では、統合モデルで販売も配送も登録に失敗し、分けたら両方通った
  • 全社で1つのモデルを作るのではなく、意味が変わる場所で区切る

ドメイン駆動設計は結局なにをする手法なのか

業務の中身をよく理解したうえで、その理解をコードの形にする手法です。用語は多いのですが、土台にあるのはこの一点だけです。

ドメイン駆動設計は、業務の手順や規則をよく理解したモデルを中心に据えて開発する手法です。ドメインとはそのソフトウェアが扱う業務の領域のことで、請求なら請求の、予約なら予約の世界を指します。

2003年のEric Evansの本から広まった考え方で、その後も実務家によって育てられてきました。特に込み入った業務ほど効くとされています。

土台は「言葉を揃える」こと

DDDの中心にあるのは、業務で使われている言葉をソフトウェアの中に埋め込むことです。Fowlerはこれをユビキタス言語と呼び、開発者と業務の担当者のあいだで共通の、厳密な言葉を作る営みだと説明しています。

なぜ厳密でなければならないかというと、ソフトウェアは曖昧さをうまく扱えないからです。会話なら「だいたい同じ意味」で流せますが、コードにするとその曖昧さがそのままバグになります

紙の上で終わらせない

モデルを作ること自体は昔からありました。DDDが強調したのは、それを紙の上ではなくソフトウェアの中で作り、動かしながら育てることです。最初に完成させて凍結するものではありません。

余談 用語の多さで身構えなくてよい

DDDには集約・値オブジェクト・リポジトリなど多くの用語がありますが、全部を一度に使う必要はありません。土台は「言葉を揃える」ことと、次の節で扱う「意味が変わる場所で区切る」ことの2つで、残りはその上に乗る道具だと編集部は考えています。

出典Martin Fowler「DomainDrivenDesign」2026-08-17 確認
Domain-Driven Design is an approach to software development that centers the development on programming a domain model that has a rich understanding of the processes and rules of a domain.
原文Martin Fowler「DomainDrivenDesign」 この内容の有効期限2027-02-17

「顧客」を1つにまとめたら、両方の部門が登録できなくなった

同じ言葉でも、部門が違えば意味が違います。それを1つのモデルに押し込むとどうなるかを、実際に書いて動かしました。

ドメイン駆動設計で最初につまずくのは、たいていここです。同じ言葉が、部門によって違うものを指しているという事実に気づかないまま設計を進めてしまいます。

Fowlerは、大きな組織では部門ごとに少しずつ違う語彙が使われる、と指摘しています。自身の経験として、電力会社で「メーター」という語が部門ごとに違う意味を持っていた例を挙げています。

実験: 販売と配送で「顧客」を共有する

手元で試しました。販売部門にとっての顧客は与信の対象で、限度額が要ります。配送部門にとっての顧客は届け先で、住所と時間帯が要ります。同じ「顧客」でも、必要な情報がまるで違います。この2つを1つのモデルにまとめてみます。

下のコードは読み飛ばして大丈夫です。統合したモデルでは、両部門ぶんの項目が全部そろっていないと受け付けない形になります。

javascript
// 統合モデル: 販売も配送も Customer 1つで扱う
function validateUnified(c) {
  const need = ['name', 'email', 'creditLimit', 'address', 'deliveryWindow'];
  return need.filter((k) => c[k] === undefined);
}

// 分割モデル: 販売の Customer と 配送の Recipient を別物にする
function validateSales(c) {
  return ['name', 'email', 'creditLimit'].filter((k) => c[k] === undefined);
}
function validateDelivery(r) {
  return ['name', 'address', 'deliveryWindow'].filter((k) => r[k] === undefined);
}
text
--- 統合モデル(Customer 1つで両方を扱う)---
  販売部門の登録: 拒否(不足: address, deliveryWindow)
  配送部門の登録: 拒否(不足: email, creditLimit)
--- 分割モデル(販売のCustomerと配送のRecipientを分ける)---
  販売部門の登録: 受理
  配送部門の登録: 受理

両方とも拒否されました。販売部門は配送情報を持っていないから、配送部門は与信情報を持っていないからです。どちらも自分の業務では正しいデータを入れているのに、登録できません。

なぜ統合すると壊れるのか

統合モデルの必須項目は、両部門の必須項目を足したものになります。するとどちらの部門も、自分には関係のない項目を埋めさせられることになります。

現場でよく起きる対処は、必須をやめて全部を任意にすることです。しかしそうすると、今度はどの項目が本当に必要なのか誰にも分からなくなります。与信の限度額が空でも通ってしまう販売システムができあがります。

統合すると必須項目が足し算になる。分けると各文脈の必須だけで済む。

統合モデル(必須5項目)L3販売の文脈(必須3項目)L2配送の文脈(必須3項目)L1依存の向き共通しているのは name だけ。残りは文脈が違えば必要な項目も違う。
図1 ── 同じ「顧客」を1つのモデルにするか、文脈ごとに分けるか

この「意味が変わる場所で区切る」という考え方が、DDDでは境界づけられたコンテキストと呼ばれます。名前は仰々しいのですが、やっていることは同じ言葉の意味が変わる線を引くだけです。

全社で1つのモデルを目指さないかつては会社全体で統一されたモデルを作るべきだと言われていました。DDDはそれを見直し、完全な統一は現実的ではないという立場を取ります。無理に1つにするより、区切って対応関係を決める方が扱いやすくなります。
出典Martin Fowler「BoundedContext」2026-08-17 確認
Different groups of people will use subtly different vocabularies in different parts of a large organization.
原文Martin Fowler「BoundedContext」 この内容の有効期限2027-02-17

小さいアプリでも取り入れる意味はあるのか

境界を分ける話は規模が大きいほど効きますが、言葉を揃えることは規模を問いません。個人開発なら後者だけでも十分に効きます。

ドメイン駆動設計の全部を個人開発に持ち込む必要はありません。ただ、言葉を揃える部分だけは規模と関係なく効きます

呼び名とコードの名前をずらさない

Fowlerは、ユビキタス言語を開発者と利用者のあいだで共通の厳密な言葉を作る営みだと説明しています。個人開発なら相手は自分ですが、半年後の自分は他人なので事情は変わりません。

実際にやることは単純です。頭の中で「申込」と呼んでいるものを、コードでは Application ではなく Order と書いていないか。会話とコードで名前がずれていたら、その分だけ読み替えの手間が発生します。

分けるべき合図はどこに出るか

境界を分けるかどうかは、規模ではなく兆候で判断します。次のような状態が見えたら、意味の違う用途が混ざっている可能性があります。

  1. 必須項目が多すぎるモデルがある。用途ごとに要るものが違うのに1つにまとめている疑い
  2. 同じ語を画面ごとに違う意味で使っている。管理画面の「ユーザー」と公開側の「ユーザー」が別物、など
  3. 使われない列が増えている。ある用途では常に空になる項目は、別の文脈のものかもしれない
  4. 条件分岐がモデルの種類で分かれているif (type === '販売') が各所に出るなら、分ける合図

逆に、これらが出ていないなら分ける必要はありません。分けること自体が目的ではないので、困っていないうちは1つのままで構いません。

設計の考え方としてはクリーンアーキテクチャの記事とも重なります。あちらは依存の向きを扱っていて、こちらは名前と境界を扱っています。

余談 分けた理由を残しておく

境界を分ける判断は、あとから見ると根拠が分かりにくくなります。なぜ分けたのかを一行でも残しておくと、次に触るときの判断が早くなります。分けた結果として重複するデータについても、どちらが正なのかを書いておくと迷いません。

出典Martin Fowler「UbiquitousLanguage」2026-08-17 確認
Ubiquitous Language is the term Eric Evans uses in Domain Driven Design for the practice of building up a common, rigorous language between developers and users.
原文Martin Fowler「UbiquitousLanguage」 この内容の有効期限2027-02-17

よくある質問

DDDは大規模なシステム向けの手法ではないのですか?
境界を分ける話は規模が大きいほど効きますが、業務の言葉をコードの名前に使うことは規模を問いません。個人開発でも、実際に使っている呼び名をそのまま関数名やテーブル名にするだけで、後から読み返したときの手間が変わります。
モデルはいくつに分ければよいのですか?
数を先に決めるものではありません。同じ言葉が場所によって違う意味を持つと気づいた時点が、分ける候補です。逆に、意味が揺れていないなら分ける必要はありません。
分けるとデータが重複しませんか?
重複します。ただし重複を避けて1つにまとめると、片方にしか要らない項目が全体の必須項目になり、この記事の実験と同じことが起きます。どちらの厄介ごとを引き受けるかの選択です。
用語が多くて覚えられません
全部を一度に使う必要はありません。業務の言葉をコードに写すことと、意味が変わる場所で区切ることの2つが土台で、他の用語はその上に乗る道具です。土台の2つだけでも効果はあります。

まとめ

  • DDDの土台は業務の言葉をコードの名前に写すこと
  • 同じ言葉が部門ごとに違う意味を持つのは普通で、統合すると必須項目が矛盾する
  • 実測では統合モデルで両部門とも登録に失敗し、分けたら両方成功した
  • 分ける基準は意味が変わる場所。数を先に決めるものではない

今日から始められること

  1. 自分のアプリで、同じ言葉が画面ごとに違う意味で使われていないか確認する
  2. 業務で使っている呼び名と、コードの名前がずれている箇所を洗い出す
  3. 必須項目が多すぎるモデルがあれば、意味の違う用途が混ざっていないか疑う
  4. 分ける判断をした理由を、コメントか設計メモに残す

実務で組んだドメイン駆動設計のワークフローには、値段が付きます

同じ課題を持つ会社にとって、動いている設定は「作る時間」を買えるということです。ServiceDockは自作のワークフローやテンプレートを出品できるマーケットプレイスです。手数料や出品の流れは出品者向けページにまとまっています。

出品の仕組みを見る