本記事について
当サイトを閲覧いただきありがとうございます。 本記事はシリーズ『生成AI時代のアーキテクチャ超入門』の「アプリケーションアーキテクチャ」カテゴリ第2弾として、ドメインロジックについて解説する記事です。
ドメインロジックは「業務固有のルール・判断・計算」を担う層で、ここの設計品質がアプリの長期競争力を決めます。本記事では Transaction Script vs Domain Model(DDD)の2大スタイル、DDD戦術/戦略パターン、貧血ドメインモデルというアンチパターン、AI時代における「業務概念を型に昇格させる」価値まで解説します。
本記事のテーマについてさらに詳しく知りたい方は『ソフトウェアアーキテクチャの基礎』も参考にしてみてください。
そもそもドメインロジックとは何か
ドメインロジックとは、ざっくり言えば「そのアプリ固有の業務ルール・判断・計算をコードで表現したもの」です。
税理士の仕事を想像してください。帳簿のフォーマット(UI)や金庫(DB)は汎用品で誰でも買えますが、「この経費は控除対象か」「消費税率は8%か10%か」を判断する専門知識は税理士固有のものです。ソフトウェアも同じで、画面表示やDB保存は汎用的な仕組みで済みますが、「割引は5000円以上で10%」「退会後30日で匿名化」といった業務ルールはそのアプリだけのものです。この層をどう設計するかがアプリの長期的な品質を決めます。
なぜドメインロジックの設計が重要なのか
もしドメインロジックの設計を曖昧にしたらどうなるか。業務ルールがUIやDBに漏れていると、同じルールが複数箇所に散らばり、変更時に片方だけ直して矛盾が生まれる、という事故がよく起きます。ここが混沌としていると、業務変更のたびにコードが歪んでいきます。
アプリの価値はドメインロジックに宿ります。UI層(画面表示・入力検証)やインフラ層(DB・外部API)は汎用的ですが、ドメイン層だけはそのアプリ固有の競争力です。
3大スタイル
Martin Fowler が整理したドメインロジックの表現方式は、大きく3つに分類できます。プロジェクトの複雑さとチームの成熟度によって、どれを採用するかが変わります。
| スタイル | 特徴 | 向くケース |
|---|---|---|
| Transaction Script | 手続き型。業務1つ=1関数で書く | CRUD中心・〜3人のMVP |
| Table Module | DBテーブルごとにロジックを集約する | レガシー改修の中間策 |
| Domain Model(DDD) | 業務概念をオブジェクトで表現する | 業務ルール50個超の複雑ドメイン |
このうちTable Moduleはあまり使われず、実務では「Transaction Script vs Domain Model」の選択になることがほとんどです。
Transaction Script
Transaction Script は、リクエストごとに処理を手続き型で書いていくスタイルです。MVCフレームワークの Service 層によく見られる形で、最もシンプルで書き始めが速いのが特徴です。
function registerOrder(req) {
const user = getUser(req.userId)
if (!user.isActive) throw new Error()
const stock = getStock(req.productId)
if (stock < 1) throw new Error()
const price = calcPrice(...)
saveOrder(...)
sendMail(...)
}
| 強み | 弱み |
|---|---|
| シンプルで書き始めが速い | ロジックがあちこちに散乱しやすい |
| 学習コストが低い | 似た処理が重複する |
| 小規模プロジェクトに向く | 業務ルールが成長するとカオス化 |
CRUD中心のシンプルな業務なら、Transaction Scriptで十分。最初からDDDにする必要はありません。
Domain Model(DDD)
Domain Model は、業務概念をクラスとして表現し、そこにロジックを集約するスタイルです。DDDはこの方式を体系化した手法で、複雑な業務ドメインを扱う時に威力を発揮します。
class Order {
place() {
if (!this.user.isActive) throw new InactiveUserError()
if (this.items.isEmpty()) throw new EmptyOrderError()
this.status = OrderStatus.Placed
return new OrderPlacedEvent(this.id)
}
}
ロジックが Order クラス内に集約されるため、「注文を出すとはどういうことか」がコード1箇所を読むだけで分かります。テストも書きやすく、業務ルールが増えても秩序を保てます。
| 強み | 弱み |
|---|---|
| ロジックが1箇所に集約・テスト容易 | 初期設計コストが高い |
| 業務の変化にコードが追従しやすい | 学習コスト大(チーム全体) |
| 大規模で複雑なドメインに強い | 小規模には過剰 |
DDDの戦術的パターン
DDDでは、ドメインモデルを構築するための戦術的パターンが整理されています。これらを知っておくと、複雑なビジネスロジックを整理された構造で表現できます。
| パターン | 役割 |
|---|---|
| Entity | IDで識別される・内部状態が可変 |
| Value Object | 値で識別される・不変(例:Money, Email) |
| Aggregate | 整合性境界を持つEntityの塊 |
| Repository | 集約を永続化する窓口 |
| Domain Service | 複数の集約にまたがるロジック |
| Domain Event | 業務上の出来事を表す |
パターンは「必要になってから導入すればよい」最初から全部使う必要はありません。
Value Objectの効用
Value Object は、プリミティブ型ではなく「意味のある型でドメインを表現」するパターンです。「金額」を number ではなく Money クラスで、「メール」を string ではなく Email クラスで表現することで、業務概念を型システムに昇格させます。
❌ sendMoney(amount: number, currency: string)
→ 引数の順序を間違えても検知できない
✅ sendMoney(amount: Money)
→ Money = amount + currency をまとめて持つ
❌ if (email.includes('@'))
→ 毎回バリデーションが必要
✅ Email.parse(str)
→ 不正値は生成時に弾くので、以降は安全
プリミティブ型を使い回す設計は「プリミティブ強迫症」(Primitive Obsession)と呼ばれるアンチパターンです。
Money や Email のようなValue Objectは規模によらず有効。プリミティブ強迫症は初期から意識する価値があります。
集約(Aggregate)
集約は、複数のEntity・Value Objectを「整合性を保つ単位」としてひとまとめにしたものです。集約の内部では強整合を保ち、集約間は結果整合にするのが設計の基本です。
[Order集約]
├─ Order(集約ルート)
├─ OrderItem[]
└─ ShippingAddress
集約を跨ぐ更新は原則禁止
→ 他集約に影響を与えるならDomainEvent経由で通知
設計原則は以下の通りです。
- 集約は小さく保つ(大きな集約は競合とロックの温床)
- 集約外への参照はIDのみ(オブジェクト直接参照を避ける)
- 集約の更新は集約ルート経由
集約境界を適切に引けるかがDDDの最大の難所。ここが失敗すると全体が破綻します。
戦略的DDD
DDDには戦術的パターン以上に重要な戦略的DDDの概念があります。これは「コードを書く前に業務の境界を発見する」ことに焦点を当てた思想で、多くのプロジェクトで軽視されがちですが、実は本当に価値があるのはこちらです。
| 概念 | 内容 |
|---|---|
| Ubiquitous Language | 業務と開発で同じ言葉を使う |
| Bounded Context | 言葉の意味が通用する範囲 |
| Context Map | 複数コンテキスト間の関係図 |
| Event Storming | 業務を付箋で可視化する発見手法 |
同じ「顧客」という言葉でも、営業部門では「見込み客」を含み、経理部門では「請求先」を意味するといった違いがあります。この違いを無理に統一せず、「コンテキストごとに別モデルとして扱う」のが戦略的DDDの核心です。
貧血ドメインモデル(アンチパターン)
DDDの形だけ真似て、「データだけ持つクラス + ロジックが全部Service層」という状態になる失敗パターンを「貧血ドメインモデル」(Anemic Domain Model)と呼びます。一見DDDっぽく見えますが、中身はTransaction Scriptと変わりません。
❌ class Order {
id, status, items // データのみ
}
class OrderService {
static place(order) {
// ロジックが全部ここにある
if (!order.user.isActive) throw ...
if (order.items.length === 0) throw ...
order.status = 'placed'
...
}
}
これはDDDの「業務ロジックは業務オブジェクトに集約する」という本質を失っており、「Transaction Scriptの複雑版」にすぎません。DDDの学習曲線が高い最大の理由でもあります。
「DDDを採用した」の95%は貧血モデル。形ではなく「ロジックの置き場所」を問うのが本質です。
ケース別の選び方
CRUD中心・シンプル業務
Transaction Script。無理にDDDを導入してもクラスが増えるだけで、業務価値は上がりません。
ロジックが複雑・業務が頻繁に変化する領域
Domain Model(DDD)。保険・金融・医療・EC・物流など、業務ルールが多い領域では投資が回収できます。
スタートアップMVP
Transaction Script → 成長後にDDD。最初から完璧な設計を目指すと力尽きます。必要になってから段階的に育てる。
既存レガシーシステムの改善
段階的にDDD化。全てを一度に書き直さず、ボトルネックの業務領域から少しずつドメインモデルに移行します。
ロジックの配置原則
業務ルールは常にドメイン層に置く。これはどのスタイルを採用していても共通の原則です。業務ルールがUI・インフラに漏れると、変更時に複数箇所を修正することになり、矛盾が生じやすくなります。
❌ コントローラで税金計算
❌ フロントエンドで割引計算(バックエンドでも再計算が必要になる)
❌ DBストアドプロシージャに業務ルール
✅ ドメイン層のValue Object / Entityに業務ルールを集約
UI側で同じ計算を「表示目的で再現する」のはOKです。ただしルールの保有者は常にドメイン層です。
業務複雑度×スタイルの実務段階表
※ 2026年4月時点の業界相場値です。テクノロジー・人材市場の変化で陳腐化するため、定期的にアップデートが必要です。
「最初からDDD」は過剰、「ずっとTransaction Script」は破綻の元。業務の複雑さに合わせて段階的に育てるのが現実解です。
| 業務複雑度 | 業務ルール数 | 推奨スタイル | 採用すべき戦術的パターン |
|---|---|---|---|
| シンプルCRUD | 〜10個 | Transaction Script | なし(素のサービス層) |
| 中程度 | 10〜50個 | Transaction Script + Value Object | Value Objectのみ |
| 複雑 | 50〜200個 | Domain Model(DDD軽量版) | Entity / Value Object / Repository |
| 極めて複雑 | 200個〜 | 本格DDD | Entity / VO / Aggregate / Domain Service / Domain Event |
判断の目安は「業務ルール変更の頻度」です。週1回以上ルールが変わる領域(保険・金融・物流・EC)はDDD投資が回収できます。一方、CRUD中心の管理画面は5年以上運用してもTransaction Scriptで十分。Martin Fowlerが2003年に整理した3スタイルの使い分けが今も有効です。
DDDは業務複雑度に見合った時だけ。早すぎる導入はクラス爆発しか生みません。
やってはいけないこと
DDDを採用したプロジェクトで事故る典型を整理します。「形だけパターンを並べても貧血モデルになるだけ」、が共通の失敗です。
| 禁じ手 | なぜダメか |
|---|---|
| 貧血ドメインモデル(Entityはデータのみ・ロジックはService層) | DDDの形だけ。Transaction Scriptの複雑版で、恩恵ゼロ |
プリミティブ強迫症(金額を number、メールを string で持つ) | 型で業務を守れない。Money / Email Value Objectにする |
| 集約(Aggregate)を大きく設計 | ロックの競合・性能劣化・テスト困難。小さく保つのが原則 |
| 集約間をオブジェクト直接参照 | 境界が破れる。集約間はID参照のみが鉄則 |
| 戦略的DDD(Ubiquitous Language / Bounded Context)を無視 | 同じ「顧客」が複数部署で別概念。文脈を切らないと破綻 |
| 業務専門家と会話せずコードだけで業務モデリング | 業務用語とコード名が乖離、「User / Member / Account / Customer」問題 |
| 最初から全ドメインにDDDを適用 | CRUD画面まで4層構造になりクラスが爆発 |
| Domain Service を乱用してロジックを置く | Entityに置くべきロジックが出ていく。まずEntity、次にDomain Service |
| Domain Event をイベントバスに直接発行 | トランザクション境界と整合しない。Outboxパターンで整合化 |
| 「DDDは宗教」と全否定する | 業務ルールが複雑で長期運用される領域では投資が確実に回収できる実用的な道具 |
| 業務複雑度を見極めずスタイルを選ぶ | CRUDにDDDは過剰、複雑業務にTransaction Scriptは破綻、業務ルール変更頻度が判断軸 |
2003年のEric Evans “Domain-Driven Design” から20年以上経っても、DDDが難しく感じるのは「技術より業務を学ぶこと」が本質だからです。パターンを覚えるより、業務専門家と会話する時間のほうが重要です。
DDDの本質は業務の言葉でモデルを育てる思想で、戦略的DDDを飛ばしてパターンだけ真似ると失敗します。
AI判断軸
| AI時代に有利 | AI時代に不利 |
|---|---|
| Value Object・Entityで業務概念を型表現 | プリミティブ型(string・number)を使い回し |
| Aggregateで整合性境界を明示 | トランザクション境界が暗黙 |
| Ubiquitous Languageをコードと対応 | 業務用語とコード名がバラバラ |
| ドメインロジックがドメイン層に集約 | ロジックがUI・Service・DBに散在 |
選定の優先順位をまとめると次の通りです。
- 業務複雑度で選ぶ(CRUDはTransaction Script、複雑業務はDDD)
- 小さく始めて段階的に育てる(最初からDDD全適用は過剰設計)
- 業務概念を型に昇格(Value Object / Aggregate / Ubiquitous Language)
- 貧血モデルを避ける(ロジックはドメイン層に集約)
DDDの用語整理がAIへのドメイン知識伝達を助ける
ユビキタス言語(開発者と業務担当が共通で使う用語体系)がコード内の型名・メソッド名に反映されていれば、AIはドメインの意図を理解した上でコードを書けます。「注文」「配送」「請求」がコード上でOrder・Shipment・Invoiceとして型定義されていれば、AIはこれらの関係性を正確に把握できます。
CRUDアプリでは過度な抽象化がAIの足を引っ張る
業務ルールが少ないCRUDアプリにDDDのフル装備(Aggregate・Repository・Domain Event等)を適用すると、AIに修正を頼む際に不要な抽象レイヤーを理解させる必要があり、かえって効率が落ちます。業務複雑度に見合ったシンプルな構造の方がAI活用の効率は高いです。
「Transaction Scriptを2ファイルに分割しただけ」(業界事例)
参画したECサイトの案件で、Order クラスは id と status と items だけを持ち、「注文を確定する」「キャンセルする」「返金する」といった処理は全て OrderService の静的メソッドに並んでいた、という事例があります。レビュー会で「これはDDDではなく、Transaction Scriptを2ファイルに分割しただけです」と指摘された、という話が聞かれます。
過去のプロジェクトで、order.place() と書けない Order クラスを書いてしまい、レビューで「このOrderはただのデータクラスで、業務は語れていない」と指摘される経験はDDDを学び始めた多くのエンジニアが通る道です。「DDDを採用した」と言いながら貧血モデルになっているプロジェクトは極めて多い。
形だけパターンを並べても、ロジックがドメインオブジェクトに宿っていなければDDDではない。order.place() が書けないコードは、まだオブジェクトではなくデータの入れ物です。
決めるべきこと — 自分のプロジェクトでの答えは?
以下の項目について、自分のプロジェクトの答えを1〜2文で言語化してみてください。曖昧なまま着手すると、必ず後から「なぜそう決めたんだっけ」が問われます。
- ドメインロジックの記述スタイル(Transaction Script / DDD / 折衷)
- Value Object をどの範囲で採用するか
- 集約の境界定義(Bounded Contextの設計)
- Domain Event / イベント駆動アーキテクチャの採用有無
- DDD戦術的パターンのうちどれを採用するか
- Ubiquitous Languageの文書化と更新ルール
この記事に関連する記事
まとめ
本記事はドメインロジックについて、Transaction Script vs DDD・Value Object・集約・戦略的DDDまで含めて解説しました。如何だったでしょうか。
業務複雑度に見合ったスタイルを選び、業務概念を型に昇格させる。これがAI時代も含めた2026年のドメインロジック設計の現実解です。
次回は命名とコード規約(命名原則・Linter/Formatter・PRレビュー・CODEOWNERS)について解説します。
シリーズ目次に戻る → 『生成AI時代のアーキテクチャ超入門』の歩き方
本記事で扱った内容の詳細は Martin Fowler - Domain Driven Design も合わせて参考にしてください。
それでは次の記事も閲覧いただけると幸いです。
📚 シリーズ:生成AI時代のアーキテクチャ超入門(27/89)
