本記事について
当サイトを閲覧いただきありがとうございます。 本記事はシリーズ『生成AI時代のアーキテクチャ超入門』の「アプリケーションアーキテクチャ」カテゴリ第2弾として、ドメインロジックについて解説する記事です。
ドメインロジックは「業務固有のルール・判断・計算」を担う層で、ここの設計品質がアプリの長期競争力を決めます。本記事ではTransaction Script vs Domain Model(DDD)の2大スタイル、貧血ドメインモデルというアンチパターン、そして「業務概念を型に昇格させる」価値まで解説します。
本記事のテーマについてさらに詳しく知りたい方は『ソフトウェアアーキテクチャの基礎』も参考にしてみてください。
この記事の結論
- 単純なCRUDはTransaction Script、複雑な業務ルールはDDDと複雑度で使い分ける
- 業務概念はValue Objectなど型に昇格させる(Money / Emailは規模によらず有効)
- DDDの全面適用は過剰。集約は本当に複雑な境界だけに使う
- 業務ルールは常にドメイン層に置く(UI・DBに漏らさない)
この記事を読む前に
本記事はプログラムの書き方・整理の仕方の話が中心です。IT用語にあまり馴染みがない方は、基礎編の「プログラムとAPIの基本」を先に読んでおくと格段に分かりやすくなると思います。また、読んでいて分からない用語が出てきたときは用語集で調べながら読み進められます。
そもそもドメインロジックとは何か
ドメインロジックとは、ざっくり言えば「そのアプリ固有の業務ルール・判断・計算をコードで表現したもの」です。
税理士の仕事を想像してください。帳簿のフォーマット(UI)や金庫(DB)は汎用品で誰でも買えますが、「この経費は控除対象か」「消費税率は8%か10%か」を判断する専門知識は税理士固有のものです。ソフトウェアも同じで、「割引は5000円以上で10%」「退会後30日で匿名化」といった業務ルールはそのアプリだけのものです。
なぜドメインロジックの設計が重要なのか
もしドメインロジックの設計を曖昧にしたらどうなるか。業務ルールがUIやDBに漏れていると、同じルールが複数箇所に散らばり、変更時に片方だけ直して矛盾が生まれる、という事故がよく起きます。ここが混沌としていると、業務変更のたびにコードが歪んでいきます。
アプリの価値はドメインロジックに宿ります。UI層やインフラ層は汎用的ですが、ドメイン層だけはそのアプリ固有の競争力です。だからこそ、業務ルールは常にドメイン層に置くのが共通原則です。コントローラで税金計算する、フロントエンドだけで割引計算する、DBのストアドプロシージャに業務ルールを埋める──いずれもルールの保有者が曖昧になる典型です。
2大スタイル ― Transaction Script vs Domain Model
Martin Fowlerが整理した表現方式のうち、実務での選択はほぼ2つに絞られます。
| スタイル | 特徴 | 向くケース |
|---|---|---|
| Transaction Script | 手続き型。業務1つ=1関数で書く | CRUD中心・〜3人のMVP |
| Domain Model(DDD) | 業務概念をオブジェクトで表現する | 業務ルール50個超の複雑ドメイン |
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中心のシンプルな業務なら、これで十分です。
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箇所を読むだけで分かります。テストも書きやすく、業務ルールが増えても秩序を保てます。代わりに初期設計コストとチーム全体の学習コストが高く、小規模には過剰です。
DDDの主要パターン
DDDの戦術的パターンは「必要になってから導入すればよい」もので、最初から全部使う必要はありません。中核は3つです。
Value Objectは、プリミティブ型ではなく意味のある型でドメインを表現するパターンです。金額を number ではなく Money で、メールを string ではなく Email で表現します。
❌ sendMoney(amount: number, currency: string)
→ 引数の順序を間違えても検知できない
✅ sendMoney(amount: Money)
→ Money = amount + currency をまとめて持つ。不正値は生成時に弾く
プリミティブ型を使い回す設計は「プリミティブ強迫症」と呼ばれるアンチパターンで、Value Objectは規模によらず初期から導入する価値があります。
集約(Aggregate)は、複数のEntity・Value Objectを「整合性を保つ単位」としてまとめたものです。集約の内部では強整合を保ち、集約間は結果整合+ID参照のみにするのが基本です。集約は小さく保ち(大きな集約は競合とロックの温床)、更新は集約ルート経由に限定します。集約境界を適切に引けるかがDDDの最大の難所です。
そして戦術パターン以上に重要なのが戦略的DDDです。業務と開発で同じ言葉を使うUbiquitous Language、言葉の意味が通用する範囲を区切るBounded Contextが核心で、同じ「顧客」でも営業部門では見込み客を含み、経理部門では請求先を意味する──この違いを無理に統一せず、コンテキストごとに別モデルとして扱います。多くのプロジェクトで軽視されがちですが、本当に価値があるのはこちらです。
貧血ドメインモデル ― 最頻出のアンチパターン
DDDの形だけ真似て、「データだけ持つクラス+ロジックが全部Service層」という状態になる失敗を「貧血ドメインモデル」と呼びます。
❌ class Order {
id, status, items // データのみ
}
class OrderService {
static place(order) {
// ロジックが全部ここにある
if (!order.user.isActive) throw ...
order.status = 'placed'
}
}
一見DDDっぽく見えますが、中身はTransaction Scriptの複雑版にすぎません。「DDDを採用した」と言うプロジェクトの多くがこの状態で、形ではなく「ロジックの置き場所」を問うのが本質です。
どう選べばいいのか ― 業務複雑度で決める
※ 2026年4月時点の業界相場値です。
「最初からDDD」は過剰、「ずっとTransaction Script」は破綻の元。業務の複雑さに合わせて段階的に育てるのが現実解です。
| 業務複雑度 | 業務ルール数 | 推奨スタイル |
|---|---|---|
| シンプルCRUD | 〜10個 | Transaction Script |
| 中程度 | 10〜50個 | Transaction Script+Value Object |
| 複雑 | 50〜200個 | Domain Model(DDD軽量版) |
| 極めて複雑 | 200個〜 | 本格DDD(Aggregate・Domain Event含む) |
判断の目安は「業務ルール変更の頻度」です。週1回以上ルールが変わる領域(保険・金融・物流・EC)はDDD投資が回収でき、CRUD中心の管理画面は5年以上運用してもTransaction Scriptで十分です。
3つのシナリオで考える
個人開発・スタートアップの場合
Transaction Scriptで最速に書いてしまいましょう。MVPの段階では業務ルール自体がまだ発見の途中ですので、先回りでドメインモデルを組んでしまうと、仮説が変わるたびにモデルの作り直しが発生してしまいます。ただ、Value Object(MoneyやEmailなど)だけは早めに導入しておくと、後の移行がかなり楽になると思います。
中小SaaSの場合
業務ルールが50個を超えてきたあたりで、Domain Model(DDDの軽量版)への段階移行を始める時期になります。とは言っても全面書き換えをする必要はなく、料金計算や在庫引当のような変更頻度の高い業務領域から、順にドメインモデルへ寄せていくのが現実解です。
大企業の場合
保険・金融・物流のような200ルールを超える複雑なドメインであれば、AggregateやDomain Eventまで含めた本格DDDの投資を回収できると考えています。レガシー改修の場合はボトルネックになっている業務領域から少しずつドメインモデルに移行して、ビッグバンの書き直しだけは避けるようにしてください。
AI判断軸 ― 型に昇格した業務概念はAIに伝わる
ユビキタス言語がAIへのドメイン知識伝達を助ける
業務用語がコード内の型名・メソッド名に反映されていれば、AIはドメインの意図を理解した上でコードを書けます。「注文」「配送」「請求」がコード上で Order・Shipment・Invoice として型定義されていれば、AIはこれらの関係性を正確に把握できます。Value Objectで業務概念を型に昇格させる設計は、人間への可読性とAIへの文脈伝達を同時に改善します。
CRUDアプリでは過度な抽象化がAIの足を引っ張る
業務ルールが少ないCRUDアプリにDDDのフル装備(Aggregate・Repository・Domain Event等)を適用すると、AIに修正を頼む際に不要な抽象レイヤーを理解させる必要があり、かえって効率が落ちます。業務複雑度に見合ったシンプルな構造の方が、AI活用の効率は高いです。
やってはいけないこと
DDDを採用したプロジェクトで事故る典型を、特に危険な6つに絞ります。
| 禁じ手 | なぜダメか → どうするか |
|---|---|
| 貧血ドメインモデル | DDDの形だけで恩恵ゼロ → ロジックをEntity / Value Objectに宿らせる |
| プリミティブ強迫症 | 型で業務を守れない → 金額はMoney、メールはEmailに昇格させる |
| 集約を大きく設計 | ロック競合・性能劣化・テスト困難 → 小さく保ち、集約間はID参照のみにする |
| 戦略的DDDを無視してパターンだけ真似る | 同じ「顧客」が部署ごとに別概念で破綻する → Bounded Contextで文脈を切る |
| 業務専門家と会話せずコードだけでモデリング | 業務用語とコード名が乖離する → ユビキタス言語を整備する |
| 最初から全ドメインにDDDを適用 | CRUD画面まで4層構造になりクラスが爆発する → 複雑な境界だけに使う |
Eric Evansの原典から20年以上経ってもDDDが難しく感じるのは、「技術より業務を学ぶこと」が本質だからです。パターンを覚えるより、業務専門家と会話する時間のほうが重要です。
筆者メモ ― 「Transaction Scriptを2ファイルに分割しただけ」
参画したECサイトの案件で、Order クラスは id と status と items だけを持ち、確定・キャンセル・返金の処理は全て OrderService の静的メソッドに並んでいた、という事例があります。レビュー会で「これはDDDではなく、Transaction Scriptを2ファイルに分割しただけです」と指摘されたそうです。
order.place() と書けない Order クラスは、まだオブジェクトではなくデータの入れ物です。形だけパターンを並べても、ロジックがドメインオブジェクトに宿っていなければDDDではありません。DDDを学び始めた多くのエンジニアが通る道です。
決めるべきこと — 自分のプロジェクトでの答えは?
以下の項目について、自分のプロジェクトの答えを1〜2文で言語化してみてください。曖昧なまま着手すると、必ず後から「なぜそう決めたんだっけ」が問われます。
- ドメインロジックの記述スタイル(Transaction Script / DDD / 折衷)
- Value Objectをどの範囲で採用するか
- 集約の境界定義(Bounded Contextの設計)
- Domain Event / イベント駆動の採用有無
- Ubiquitous Languageの文書化と更新ルール
この記事に関連する記事
まとめ
本記事はドメインロジックについて、Transaction Script vs DDD・Value Object・集約・戦略的DDDまで含めて解説しました。如何だったでしょうか。
業務複雑度に見合ったスタイルを選び、業務概念を型に昇格させる。これがAI時代も含めた2026年のドメインロジック設計の現実解です。
次回は命名とコード規約(命名原則・Linter/Formatter・PRレビュー・CODEOWNERS)について解説します。
シリーズ目次に戻る → 『生成AI時代のアーキテクチャ超入門』の歩き方
本記事で扱った内容の詳細は Martin Fowler - Domain Driven Design も合わせて参考にしてください。
それでは次の記事も閲覧いただけると幸いです。
📚 シリーズ:生成AI時代のアーキテクチャ超入門(33/95)
