7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

いまさらドメイン駆動設計(DDD)とオブジェクト指向(OOP)

Last updated at Posted at 2025-12-18

この記事は、ラクス Advent Calendar 2025の18日目の記事です。昨日は、@udonrmPHPのRFCを読むの記事でした。

前書き

最近はAIを活用した開発が活発で、直接コードを書くことが少なくなってきた人も多いと思います。ただ、既存サービスでAIを活用する上では既存コードの整合性も気にする必要があり、レビュワーによるクラス設計の理解が重要になってきます。

私たちの開発チームではドメイン駆動設計を基本的な設計手法として採用しているのですが、直近メンバー内でオブジェクト指向とドメイン駆動設計の違いをあまり理解していなさそうな会話があったため、改めてオブジェクト指向とドメイン駆動設計について整理していきたいと思います。

なお、筆者はJava言語を前提とした内容を記載しているため、他言語については他の解釈もあるかと思うのでご注意ください。

オブジェクト指向とは

オブジェクト指向では、文字通り 「オブジェクト(モノ)」 を中心に考えます。オブジェクトは 「属性(データ)」 と 「振る舞い(ロジック)」 の2つの要素を持ちます。

例えば、「銀行口座」を例に考えると以下のように整理できます。

  • オブジェクト: 銀行口座
  • 属性: 口座番号, 残高
  • 振る舞い: 入金, 出金, 残高照会

これらをプログラムに落とし込む際、Javaでは以下のようなクラスとして表現されます。

public class BankAccount {
    private String accountNumber; // 属性: 口座番号
    private long balance; // 属性: 残高

    public BankAccount(String accountNumber, long balance) {
        this.accountNumber = accountNumber;
        this.balance = balance;
    }

    // 振る舞い: 入金
    public void deposit(long amount) {
        balance += amount;
    }

    // 振る舞い: 出金
    public void withdraw(long amount) {
        if (amount <= balance) {
            balance -= amount;
        } else {
            System.out.println("残高不足");
        }
    }

    // 振る舞い: 残高照会
    public long showBalance() {
        return balance;
    }
}

ここでのポイントは、状態を変化させるのはあくまでオブジェクト自身であり、外部から直接状態を操作させない点です。これは 「カプセル化」 と呼ばれる手法です。 カプセル化によって、内部ロジックを変更しても外部への影響を最小限に抑えることができ、保守性が向上します。

ドメイン駆動設計(DDD)とは

一方、ドメイン駆動設計(DDD)とは、業務(ドメイン)を深く理解し、その知識を設計とコードに直接反映させるための設計思想です。

「このシステムで最も解決すべき課題(コア・ドメイン)はどこか?」に着目し、エンジニアとドメインエキスパートが共通言語(ユビキタス言語)を用いて、業務上の境界線(境界づけられたコンテキスト)を引いていきます。

DDDはプログラミング技法だと思われがちですが、本質的には 「設計」の手法 です。そのため、定義された概念は非エンジニアである経営者やドメインエキスパートとも共有できるものになります。

ドメイン駆動設計をプログラミングに落とし込むと

ドメイン駆動設計はあくまで設計手法だと書きましたが、とはいえ実際にはドメイン駆動設計をプログラミングに適用することが多いと思います。よく扱われるクラス設計には以下のようなものがあります。

1. 値オブジェクト (Value Object)

「お金」を単なる long 型で扱うと、誤って「重さ」や「個数」と足し算できてしまいます。これを防ぐために「型」として定義します。値オブジェクトは交換可能で、中身が同じなら同一とみなします。

public final class Money {
    private final long amount;

    public Money(long amount) {
        if (amount < 0) throw new IllegalArgumentException("金額は0以上である必要があります");
        this.amount = amount;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;

        if (o == null || getClass() != o.getClass()) return false;

        Money money = (Money) o;
        return amount == money.amount && Objects.equals(currency, money.currency);
    }

    @Override
    public int hashCode() {
        return Objects.hash(amount, currency);
    }

    public Money add(Money other) {
        return new Money(this.amount + other.amount);
    }
}

2. エンティティ (Entity)

「銀行口座」のように、属性(残高)が変わっても、識別子(口座番号)によって同一性が保たれるものをエンティティと呼びます。

public class BankAccount {
    private final String accountNumber;
    private Money balance;

    public BankAccount(String accountId, Money balance) {
        this.accountId = accountId;
        this.balance = initialBalance;
    }

    public void deposit(long amount) {
        balance += amount;
    }

    public void withdraw(long amount) {
        if (amount <= balance) {
            balance -= amount;
        } else {
            System.out.println("残高不足");
        }
    }

    public Money showBalance() {
        return balance;
    }
}

3. ドメインサービス(Domain Service)

「送金」のように、複数のエンティティを跨ぐ処理や、エンティティ自身の責務とするには不自然なロジックを扱うためのクラスです。

public class BankTransfer {
    public void transfer(BankAccount from, BankAccount to, Money amount) {
        from.withdraw(amount);
        to.deposit(amount);
    }
}

両者の関係:ドメイン駆動設計のプログラミングはオブジェクト指向をベースにしている

よく見てみると、オブジェクト指向で説明した「銀行口座」とDDDで説明した「銀行口座」は値オブジェクトを利用していること以外は同じになっています。(値オブジェクトもDDD限定の実装方法ではありません)

お気づきの方もいるかもしれませんが、DDDの実装パターンはオブジェクト指向の原則をより厳格に適用したものです。

DDDは「何を(What)」設計するかという戦略であり、オブジェクト指向はそれを「どう(How)」実現するかという 戦術(手段) にあたります。

オブジェクト指向が伴わないドメイン駆動設計の例

オブジェクト指向の理解が不十分なまま、形だけDDDを取り入れた例を考えてみます。(今回初めて聞いたのですが「ドメインモデル貧血症」というアンチパターンだそうです。へぇー)

以下のように、データ(エンティティ)とロジック(サービス)が切り離され、データが「ただの入れ物」になってしまう状態です。

public class BankAccount {
    public String accountNumber;
    public Money balance;
}
public class BankAccountService {
    public void transfer(BankAccount from, BankAccount to, long amount) {
        if (from.balance >= amount) {
            from.balance -= amount;
            to.balance += amount;
        } else {
            throw new RuntimeException("残高不足");
        }
    }
}

これでは、BankAccount の整合性を保つ責任が、それを使うすべてのサービス側に分散してしまい、保守性が低下します。

まとめ

  • ドメイン駆動設計: 複雑なビジネス課題をソフトウェアに正しく反映させるための「設計思想(戦略)」
  • オブジェクト指向: プログラムを整理し、変更に強くするための「実装手法(戦術)」

ドメイン駆動設計はソフトウェア開発を行う上での有効な設計手段ですが、実際のプログラミングに落とし込むうえではオブジェクト指向のような具体的な実装手段が必要になってきます。

ドメイン駆動設計を採用してみたけど、いまいち使いこなせていないな、実装負債溜まっていくな、といった心当たりがある方は一度オブジェクト指向を理解できているか見直してみてください。

今回はドメイン駆動設計とオブジェクト指向の関係性に着目したかったため省きましたが、オブジェクト指向には実務で使える様々なデザインパターンが存在します。

以下の書籍がおすすめなので、デザインパターンについて勉強したい方は読んでみてください。(第2版しか読んだことないですが大枠は変わっていないはず)

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?