TypeScript上級

上級 TypeScriptで学ぶドメイン駆動設計|ケーススタディ編

導入

本記事では、上級者向けにドメイン駆動設計(DDD)の実践的なアプローチを取り上げます。架空のプロジェクトとして、オンライン書店のシステムを考え、その中でどのようにDDDを適用するかを探ります。このシナリオを通じて、ドメインの理解を深め、実際のビジネスロジックにどのように落とし込むかを示します。

教科書レベルの解説(ドメイン駆動設計)

重要な概念の整理

ドメイン駆動設計は、ソフトウェア開発においてビジネスの核心を捉え、システムの設計をそのドメインモデルに基づいて行う手法です。以下の概念が特に重要です:

  • エンティティ:一意に識別されるオブジェクト。例えば、書籍やユーザーなど。
  • 値オブジェクト:属性の集合で、一意の識別子を持たないオブジェクト。例えば、書籍の著者情報。
  • アグリゲート:関連するエンティティや値オブジェクトの集まり。整合性の境界を定義します。
  • リポジトリ:エンティティやアグリゲートの永続化を担当するコンポーネント。

コード例(TypeScript)


class Book {
    constructor(public id: string, public title: string, public author: Author) {}
}

class Author {
    constructor(public name: string, public biography: string) {}
}

class BookRepository {
    private books: Book[] = [];

    add(book: Book) {
        this.books.push(book);
    }

    findById(id: string): Book | undefined {
        return this.books.find(book => book.id === id);
    }
}

コードの行ごとの解説

  1. class Book { ... }:書籍を表すエンティティ。ID、タイトル、著者を属性として持つ。

  2. class Author { ... }:著者を表す値オブジェクト。名前と経歴を持つ。

  3. class BookRepository { ... }:書籍の永続化を担当するリポジトリ。書籍の追加と検索機能を提供。

  4. add(book: Book) { ... }:書籍をリポジトリに追加するメソッド。

  5. findById(id: string): Book | undefined { ... }:IDで書籍を検索するメソッド。見つからない場合はundefinedを返す。

ケーススタディ編

架空のオンライン書店プロジェクトでは、書籍の管理機能を実装することが求められました。ドメイン駆動設計の原則に従い、書籍と著者をエンティティと値オブジェクトとして定義しました。リポジトリを通じて、書籍の追加や検索を行うことで、ドメインの整合性を保ちます。

ここでの落とし穴は、エンティティと値オブジェクトの境界を曖昧にしてしまうことです。例えば、著者情報をエンティティとして扱うと、著者の情報が変更されるたびに書籍を更新する必要が生じ、整合性が崩れる恐れがあります。これを避けるためには、著者を値オブジェクトとして扱い、書籍エンティティの中に埋め込む形を取ることが推奨されます。

まとめ

  • ドメイン駆動設計はビジネスの核心を捉える手法であり、エンティティ、値オブジェクト、リポジトリの明確な定義が重要。
  • エンティティと値オブジェクトの役割を明確に分けることで、システムの整合性を保つことができる。
  • 実際のビジネスシナリオに基づいた設計が、ドメイン駆動設計の真価を引き出す。