ドメイン駆動設計(DDD)入門 – ビジネスロジックを正しくモデリングする

「20年コードを書いてきたのに、なぜ仕様変更のたびにシステムが壊れるのか?」

その答えは、「ビジネスの本質がコードに反映されていない」からです。

45歳のあなたは、これまで何度も経験してきたはずです。顧客の要望を聞き、設計書を書き、コードを実装する。しかし、3ヶ月後の仕様変更で、あちこちのコードを修正する羽目になる。「なぜこんなに脆いのか?」と。

それは、技術力の問題ではありません。ビジネスロジックとコードが乖離していることが原因です。

ドメイン駆動設計(DDD)は、この問題を根本から解決する設計手法です。顧客のビジネス(ドメイン)を深く理解し、その本質をコードに落とし込む。これができれば、仕様変更に強く、長期的に保守可能なシステムが作れます。

そして、この「ビジネスを理解してシステム化する力」こそ、あなたが目指す上流工程で評価されるスキルなのです。

この記事では、通勤時間と夜の1時間で、3ヶ月後にはDDDの基礎を理解し、面接で説明できるレベルになるためのロードマップをお伝えします。完璧を目指す必要はありません。まずは「ビジネスとコードをつなぐ思考法」を身につけましょう。


目次

第1章:なぜ今、ドメイン駆動設計なのか?

結論

DDDは、上流工程への転職で最も差別化できるスキルです。

理由

転職市場では、「コードが書ける人」は飽和していますが、「ビジネス要件を正しく理解し、システムに落とし込める人」は圧倒的に不足しています。

特に40代の転職では、**若手より優れた「ビジネス理解力」**が評価されます。DDDを学ぶことで、「このシステムは、顧客のどんなビジネス課題を解決するのか?」という本質的な問いに答えられるようになります。

なぜなら、DDDは単なる設計パターンではなく、**「ビジネス専門家とエンジニアが共通言語で対話する方法論」**だからです。

具体例

44歳でSIerからWeb系企業のテックリードに転職したYさんは、こう語ります。

「面接で『在庫管理システムをどう設計しますか?』と聞かれました。私は『まず、ビジネス上の”在庫”とは何かを定義します。単なる数量ではなく、予約済み在庫、破損在庫などの状態があるはずです。それをドメインモデルとして表現し…』と説明したところ、面接官の目の色が変わりました。『まさにDDDの考え方ですね。ぜひ来てほしい』と即決でした。年収は500万円から720万円に上がりました」

DDDを学ぶことは、ビジネスとコードの通訳者になることです。これこそ、上流工程で求められる本質的なスキルなのです。

まとめ

DDDは、あなたの20年の経験を「ビジネス理解力」に昇華させる最強の武器です。今日から学習を始めることで、3ヶ月後には転職面接で圧倒的に差別化できます。


第2章:ドメイン駆動設計とは何か? – 3つの核心原則

結論

DDDの本質は、「ビジネスの言葉でコードを書く」ことです。

理由

従来のシステム開発では、ビジネス側が「顧客」「注文」と呼ぶものを、エンジニアが勝手に「User」「Order」というテーブル設計に落とし込んでいました。

しかし、ビジネス側の「顧客」には、「見込み客」「既存顧客」「休眠顧客」といった状態があり、それぞれで扱いが異なります。このビジネスルールがコードに反映されていないことが、仕様変更に弱いシステムを生む原因です。

DDDでは、以下の3つの原則で、ビジネスとコードを一致させます。

具体例

原則1:ユビキタス言語(共通言語)

ビジネス側とエンジニアが、同じ言葉でシステムを語る。

// ❌ 悪い例:ビジネス用語が失われている
class User {
  int status; // 0=見込み、1=既存、2=休眠 (コメントでしか分からない)
}

// ⭕ 良い例:ビジネスの言葉がコードに現れている
class Customer {
  CustomerStatus status; // enum { PROSPECT, ACTIVE, DORMANT }
  
  boolean isActive() {
    return status == CustomerStatus.ACTIVE;
  }
}

原則2:境界づけられたコンテキスト

「顧客」という言葉も、営業部門と経理部門では意味が異なります。DDDでは、文脈ごとにモデルを分けます。

  • 営業コンテキスト:顧客 = 見込み度、商談履歴
  • 経理コンテキスト:顧客 = 請求先、支払い履歴

原則3:ドメインモデル

ビジネスルールをオブジェクトに閉じ込め、データと振る舞いを一体化させます。

// ❌ 貧血モデル:データだけでビジネスロジックが外部に散在
class Order {
  int totalAmount;
}
// 別のサービスクラスで計算ロジック
orderService.calculateTotal(order);

// ⭕ ドメインモデル:ビジネスルールがオブジェクト内に
class Order {
  private List<OrderItem> items;
  
  int calculateTotal() {
    return items.stream()
      .mapToInt(item -> item.getPrice() * item.getQuantity())
      .sum();
  }
  
  void addItem(OrderItem item) {
    if (this.status == OrderStatus.SHIPPED) {
      throw new IllegalStateException("出荷済み注文には追加できません");
    }
    items.add(item);
  }
}

まとめ

DDDの3原則を理解すれば、「なぜこの設計が優れているのか?」を面接で説明できるようになります。まずはこの考え方を腹落ちさせることから始めましょう。

関連記事

バックエンドAPI設計の実践技法 – RESTful/GraphQL設計とOpenAPI仕様書作成
DDDで設計したドメインモデルを、どうAPIとして公開するかを学べます。


第3章:DDDの戦術的パターン – エンティティと値オブジェクト

結論

DDDの具体的な実装は、エンティティと値オブジェクトの使い分けから始まります。

理由

ビジネスの世界には、「同一性が重要なもの(エンティティ)」と「値そのものが重要なもの(値オブジェクト)」の2種類があります。

この区別ができないと、全てをIDで管理する肥大化したテーブル設計になり、ビジネスルールがコードから消えてしまいます。

具体例

エンティティ:同一性が重要

「顧客」は、名前や住所が変わっても「同じ顧客」です。IDで識別します。

class Customer {
  private CustomerId id; // 一意識別子
  private String name;
  private Address address;
  
  // nameやaddressが変わっても、同じCustomer
  void changeName(String newName) {
    this.name = newName;
  }
}

値オブジェクト:値そのものが重要

「住所」は、同じ値なら同じ住所です。IDは不要。

class Address {
  private final String prefecture;
  private final String city;
  private final String street;
  
  // 不変オブジェクト(値が変わったら別のAddressを作る)
  Address(String prefecture, String city, String street) {
    if (prefecture == null || prefecture.isEmpty()) {
      throw new IllegalArgumentException("都道府県は必須です");
    }
    this.prefecture = prefecture;
    this.city = city;
    this.street = street;
  }
  
  // 等価性は値で判断
  @Override
  boolean equals(Object obj) {
    if (!(obj instanceof Address)) return false;
    Address other = (Address) obj;
    return this.prefecture.equals(other.prefecture)
        && this.city.equals(other.city)
        && this.street.equals(other.street);
  }
}

値オブジェクトの威力

値オブジェクトを使うと、ビジネスルールをカプセル化できます。

class Money {
  private final int amount;
  private final Currency currency;
  
  Money add(Money other) {
    if (!this.currency.equals(other.currency)) {
      throw new IllegalArgumentException("通貨が異なります");
    }
    return new Money(this.amount + other.amount, this.currency);
  }
}

// 使う側
Money price1 = new Money(1000, Currency.JPY);
Money price2 = new Money(500, Currency.JPY);
Money total = price1.add(price2); // 安全に計算

まとめ

エンティティと値オブジェクトの使い分けは、DDDの最初の実践ステップです。まずは既存のコードを見直し、「これはエンティティか? 値オブジェクトか?」と問いかけてみましょう。


第4章:集約(Aggregate)でビジネス整合性を守る

結論

集約は、ビジネスルールの境界線を定義するDDDの最重要パターンです。

理由

複雑なシステムでは、複数のオブジェクトが関連し合います。しかし、全てのオブジェクトを自由に操作できると、ビジネスルールが守れなくなります。

例えば、「注文明細を直接削除すると、注文全体の合計金額が狂う」といった問題です。

集約は、「一緒に変更されるべきオブジェクトの塊」を定義し、外部からは集約ルート経由でのみ操作させることで、整合性を保証します。

具体例

集約の設計例:注文システム

// 集約ルート
class Order {
  private OrderId id;
  private List<OrderItem> items; // 集約内部のオブジェクト
  private OrderStatus status;
  
  // 外部からはOrderを経由してのみItemを操作できる
  void addItem(Product product, int quantity) {
    if (status == OrderStatus.SHIPPED) {
      throw new IllegalStateException("出荷済みには追加できません");
    }
    OrderItem item = new OrderItem(product, quantity);
    items.add(item);
  }
  
  void removeItem(OrderItemId itemId) {
    if (status == OrderStatus.SHIPPED) {
      throw new IllegalStateException("出荷済みからは削除できません");
    }
    items.removeIf(item -> item.getId().equals(itemId));
  }
  
  // 集約全体の整合性を保つ
  void ship() {
    if (items.isEmpty()) {
      throw new IllegalStateException("明細がない注文は出荷できません");
    }
    this.status = OrderStatus.SHIPPED;
  }
}

// 外部からは直接OrderItemを操作できない(privateフィールド)
class OrderItem {
  private OrderItemId id;
  private Product product;
  private int quantity;
  
  // パッケージプライベート(Orderからのみ生成可能)
  OrderItem(Product product, int quantity) {
    if (quantity <= 0) {
      throw new IllegalArgumentException("数量は1以上です");
    }
    this.product = product;
    this.quantity = quantity;
  }
}

集約設計の3原則

  1. 小さく保つ:1つの集約には、本当に一緒に変更されるものだけを含める
  2. IDで参照する:集約間は、オブジェクト参照ではなくIDで関連付ける
  3. トランザクション境界:1回のトランザクションで更新するのは1つの集約のみ

まとめ

集約を理解すれば、「なぜこの設計が保守しやすいのか?」を論理的に説明できます。面接で「集約とは?」と聞かれたら、「ビジネスルールを守る境界線」と答えましょう。

関連記事

マイクロサービスアーキテクチャの実践 – 分散システム設計の利点と課題
集約の考え方は、マイクロサービスの境界設計にも応用できます。


第5章:リポジトリパターンでデータ永続化を抽象化する

結論

リポジトリは、ビジネスロジックとデータベースを分離するための重要パターンです。

理由

従来の設計では、ビジネスロジック内にSQLが散在し、データベース変更のたびに大規模な修正が必要でした。

リポジトリパターンを使えば、「データの保存・取得」を抽象化し、ビジネスロジックは「何を保存するか」だけに集中できます。

具体例

リポジトリインターフェース

// ドメイン層(ビジネスロジック)に配置
interface OrderRepository {
  Order findById(OrderId id);
  void save(Order order);
  List<Order> findByCustomerId(CustomerId customerId);
}

// ビジネスロジック(ドメインサービス)
class OrderService {
  private OrderRepository orderRepository;
  
  void shipOrder(OrderId orderId) {
    Order order = orderRepository.findById(orderId);
    order.ship(); // ドメインロジック
    orderRepository.save(order); // 保存はリポジトリに委譲
  }
}

実装はインフラ層に

// インフラ層(データベースアクセス)
class JpaOrderRepository implements OrderRepository {
  @Override
  public Order findById(OrderId id) {
    // JPAやMyBatisでDBアクセス
    OrderEntity entity = entityManager.find(OrderEntity.class, id.getValue());
    return toDomain(entity); // エンティティをドメインモデルに変換
  }
  
  @Override
  public void save(Order order) {
    OrderEntity entity = toEntity(order);
    entityManager.persist(entity);
  }
}

リポジトリの利点

  1. テストが簡単:モックリポジトリで、DBなしでビジネスロジックをテスト
  2. DB変更に強い:MySQLからPostgreSQLへの移行も、リポジトリ実装だけ変更
  3. ビジネスロジックが純粋:SQLの詳細から解放される

まとめ

リポジトリパターンは、DDDの実践で最初に導入すべきパターンです。既存のDAOをリポジトリに置き換えることから始めましょう。

関連記事

データベース設計のベストプラクティス – 正規化から非正規化までの判断基準
DDDのドメインモデルを、どうデータベースに落とし込むかを学べます。


第6章:ドメインサービスで複雑なビジネスロジックを表現する

結論

エンティティや値オブジェクトに収まらないロジックは、ドメインサービスに切り出します。

理由

ビジネスロジックの中には、「複数のエンティティをまたがる処理」や「外部サービスとの連携」など、単一のオブジェクトに属さないものがあります。

これを無理にエンティティに詰め込むと、責務が不明確になり、保守性が下がります。

具体例

ドメインサービスが必要なケース

// ❌ 悪い例:Orderに無理やり詰め込む
class Order {
  void ship(InventoryService inventoryService, ShippingService shippingService) {
    // 在庫確認
    inventoryService.checkStock(this.items);
    // 配送手配
    shippingService.arrange(this.deliveryAddress);
    this.status = OrderStatus.SHIPPED;
  }
}
// → Orderが外部サービスに依存し、テストしづらい

// ⭕ 良い例:ドメインサービスに切り出す
class OrderShippingService {
  private InventoryChecker inventoryChecker;
  private ShippingArranger shippingArranger;
  
  void ship(Order order) {
    // 在庫確認
    if (!inventoryChecker.hasStock(order.getItems())) {
      throw new OutOfStockException("在庫不足です");
    }
    
    // 配送手配
    ShippingRequest request = shippingArranger.arrange(order.getDeliveryAddress());
    
    // Orderの状態変更
    order.ship(request.getTrackingNumber());
  }
}

ドメインサービスの特徴

  1. ステートレス:内部状態を持たない(依存サービスはコンストラクタで注入)
  2. ビジネス用語で命名:「OrderShippingService」「PricingService」など
  3. ユビキタス言語の一部:ビジネス側と同じ言葉で語れる

まとめ

ドメインサービスを使えば、複雑なビジネスロジックも、読みやすく保守しやすいコードで表現できます。「この処理は誰の責務か?」を常に問いましょう。


第7章:実践的なDDD学習ロードマップ(3ヶ月計画)

結論

DDDは、理論と実践を交互に繰り返すことで身につきます。

理由

DDDは、本を読むだけでは理解できません。「自分でドメインモデルを考え、コードを書く」プロセスが不可欠です。

以下の3ヶ月計画で、段階的にスキルを習得しましょう。

具体例

第1ヶ月:基礎理解とパターン習得

Week 1-2: 書籍とオンライン講座で基礎学習

  • 通勤時間:Kindle Unlimitedで「エリック・エヴァンスのドメイン駆動設計」を読む
  • 夜30分:Udemy講座「ドメイン駆動設計入門」を視聴

Week 3-4: 小さなコード演習

  • 題材:「図書館システム」「在庫管理システム」
  • エンティティ、値オブジェクト、集約を実装
  • GitHubにコミット

第2ヶ月:実践プロジェクトで応用

Week 5-6: 実務に近い題材で設計

  • 題材:「ECサイトの注文管理」「予約システム」
  • ユビキタス言語の洗い出し
  • 境界づけられたコンテキストの定義

Week 7-8: コード実装とレビュー

  • リポジトリパターンの実装
  • ドメインサービスの切り出し
  • オンラインコミュニティでコードレビュー依頼

第3ヶ月:ポートフォリオ作成と面接準備

Week 9-10: オリジナルアプリ開発

  • 自分が興味ある領域でドメインモデリング
  • 「なぜこの設計にしたか」のドキュメント作成

Week 11-12: 面接対策

  • DDDの説明を30秒、3分、10分バージョンで準備
  • GitHubのREADMEを充実させる

まとめ

3ヶ月後、あなたは「DDDで設計したアプリ」をポートフォリオとして示せるようになります。これが、上流工程への扉を開く鍵です。

【おすすめ学習教材】

関連記事

APIファーストな開発手法 – フロント・バックエンド分離とスキーマ駆動開発
DDDで設計したドメインを、どうAPIとして公開するかを学べます。


第8章:DDDを学ぶ上での「よくある誤解」と対策

結論

DDDは「大規模システム専用」ではなく、小規模でも価値がある設計手法です。

理由

多くの人が「DDDは難しすぎる」「大企業向け」と誤解していますが、本質は**「ビジネスの言葉でコードを書く」**というシンプルな考え方です。

以下の誤解を解消しましょう。

具体例

誤解1:「DDDは大規模プロジェクトでないと意味がない」

真実:小規模でも、値オブジェクトやリポジトリパターンは有効

例えば、ToDoアプリでも:

// 値オブジェクトで制約を表現
class TaskTitle {
  private final String value;
  
  TaskTitle(String value) {
    if (value.length() > 100) {
      throw new IllegalArgumentException("タイトルは100文字以内");
    }
    this.value = value;
  }
}

これだけで、ビジネスルールがコードに反映されます。

誤解2:「完璧にDDDを適用しないと意味がない」

真実:部分的な導入でも効果あり

  • まずは値オブジェクトから始める
  • 次にリポジトリパターンを導入
  • 徐々に集約を整理

完璧主義は禁物です。小さく始めましょう。

誤解3:「DDDを学ぶには何年もかかる」

真実:基礎は3ヶ月で習得可能

  • 1ヶ月:パターンの理解
  • 2ヶ月:小規模プロジェクトで実践
  • 3ヶ月:面接で説明できるレベル

20年の開発経験があるあなたなら、若手より速く理解できます。

まとめ

DDDは「完璧に実践」するものではなく、「ビジネスとコードを近づける」ための考え方です。まずは1つのパターンから始めましょう。

関連記事

レガシーコードのリファクタリング戦略 – 技術的負債を計画的に解消する
既存のコードにDDDを導入する方法を学べます。


第9章:DDDを面接でどう説明するか? – 3つの回答例

結論

面接では、「DDDとは?」を30秒で説明できる準備をしましょう。

理由

上流工程の面接では、「設計手法を知っているか?」だけでなく、「なぜその手法が優れているか?」を論理的に説明する力が問われます。

以下の3パターンで準備してください。

具体例

パターン1:30秒版(エレベーターピッチ)

「DDDは、ビジネスの専門用語をそのままコードに反映する設計手法です。例えば、顧客を『User』ではなく『Customer』とクラス名にし、ビジネスルールを『CustomerService』に実装します。これにより、仕様変更に強く、長期保守可能なシステムが作れます」

パターン2:3分版(面接での詳細説明)

「DDDの核心は、ユビキタス言語です。ビジネス側が『注文』と呼ぶものを、エンジニアが『Order』というクラスで表現し、『出荷する』というビジネスルールをship()メソッドで実装します。

さらに、集約パターンで整合性を保証します。例えば、注文明細を直接操作させず、必ず注文経由で変更させることで、『出荷済みには追加できない』というルールを強制できます。

私は過去のプロジェクトで、仕様変更のたびに大量のSQLを書き換える苦労をしました。DDDを学んだ今なら、ビジネスロジックをドメインモデルに閉じ込め、データベース変更の影響を最小化できます」

パターン3:10分版(ホワイトボードで図解)

面接官に「実際に設計してみてください」と言われたら:

  1. ユビキタス言語の洗い出し:「このシステムの主要な概念は?」
  2. エンティティと値オブジェクトの分類:「顧客はエンティティ、住所は値オブジェクト」
  3. 集約の境界を定義:「注文と注文明細は1つの集約」
  4. リポジトリで永続化:「OrderRepositoryで保存・取得」

図を描きながら説明できれば、圧倒的に説得力が増します。

まとめ

面接での説明力は、実際にコードを書いた経験から生まれます。ポートフォリオを作り、「このコードはなぜこう設計したか」を言語化しておきましょう。

関連記事

システム設計面接対策とケーススタディ – スケーラビリティを考慮した設計力
DDDを含む、システム設計面接の総合対策を学べます。


第10章:今日から始める3つの行動

結論

この記事を読んだ「今」が、設計力を高める最後のチャンスです。

理由

DDDは、読むだけでは身につきません。「今日から手を動かす」ことが、3ヶ月後の成長を決めます。

以下の3つの行動から始めてください。

具体例

STEP1:Udemy講座を購入する(所要時間:10分)

「いつか学ぼう」ではなく、今すぐ購入してください。購入した瞬間、学習へのコミットメントが生まれます。

おすすめ:Udemy – ドメイン駆動設計入門

STEP2:Kindle Unlimitedで技術書を読み始める(所要時間:15分)

通勤時間を学習時間に変えましょう。30日間無料体験で、今日から読めます。

おすすめ:Kindle Unlimited無料体験 – 「エリック・エヴァンスのドメイン駆動設計」「実践ドメイン駆動設計」が読める

STEP3:小さなコードを書く(所要時間:30分)

今夜、値オブジェクトを1つ実装してみてください。

class Email {
  private final String value;
  
  Email(String value) {
    if (!value.matches("^[\\w-\\.]+@[\\w-]+\\.[a-z]{2,}$")) {
      throw new IllegalArgumentException("無効なメールアドレス");
    }
    this.value = value;
  }
}

この1つのクラスが、あなたのDDD学習の第一歩です。

まとめ

この3つのステップは、今日中に完了できます。明日から、あなたは「DDDを学んでいるエンジニア」になっています。

【今すぐ始める学習セット】

関連記事

ユーザーストーリーマッピングの実践 – 顧客視点でプロダクトを構想する
DDDの「ビジネス理解」を、要件定義レベルまで深めましょう。

テスト駆動開発(TDD)の始め方 – ユニットテストから統合テストまでの実践
DDDで設計したドメインモデルを、TDDでテストする方法を学べます。


まとめ

ドメイン駆動設計習得ロードマップの全体像

第1ヶ月:基礎理解 → ユビキタス言語、エンティティ、値オブジェクトを理解

第2ヶ月:パターン習得 → 集約、リポジトリ、ドメインサービスを実装

第3ヶ月:実践と面接準備 → ポートフォリオ作成、説明力の強化

3ヶ月後:転職活動 → 「ビジネスを理解して設計できる上流エンジニア」として応募

最後に:45歳のあなたへ

「設計なんて、若い人がやればいい」——その考えは、今日で捨ててください。

あなたには20年の開発経験があります。その経験があるからこそ、「仕様変更で何度もコードを書き換えた苦しみ」を知っています。その苦しみこそが、DDDの価値を深く理解する武器になるのです。

若手が「DDDはパターンだ」と暗記している間に、あなたは「なぜこの設計が保守しやすいのか」を、実体験から語れます。

行動しなければ、何も変わりません。

でも、今日Udemyで講座を1つ買い、今夜30分だけ値オブジェクトを書けば、明日のあなたは「DDDを学び始めた設計者」になっています。

3ヶ月後、あなたは「ビジネスとコードをつなぐ上流エンジニア」として、年収700万円以上のオファーを手にしているはずです。

その第一歩を、今日、踏み出しましょう。

【今日から始める学習セット – 最後のご案内】

  • Udemy講座:DDD、API設計、システム設計まで幅広くカバー
  • Kindle Unlimited:30日間無料体験。通勤時間が成長の時間に変わります
  • Audible:「ながら学習」で学習時間を2倍に
  • Notion:ドメインモデルの整理、学習ログの記録に最適

関連記事

マイクロサービスアーキテクチャの実践 – 分散システム設計の利点と課題
DDDの境界づけられたコンテキストを、マイクロサービスに応用する方法を学べます。

ビジネスモデルキャンバス活用法 – 収益構造を可視化して事業を設計
将来の起業も視野に、ビジネス全体の設計力を高めましょう。


Todd

あなたの成功を、心から応援しています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次