DDD視点で作成した設計書サンプル(詳細版)

DDD

スポンサーリンク
google.com, pub-5238665064291805, DIRECT, f08c47fec0942fa0

はじめに

QUOカード デジタルイノベーションラボのコーディングテスト記事を題材に、DDD(ドメイン駆動設計)を意識した設計書サンプルをまとめました。設計方針についてはこの記事を参照してください。

本記事では同記事を参考にさせていただき、必要最小限の機能だけを設計に反映しています。実装詳細は扱いません。


ドメイン概要

ドメインは、著者(Author)と書籍(Book)を管理する。提供する能力は 著者の登録・更新、書籍の登録・更新、および 特定の著者に紐づく書籍一覧の取得。著者と書籍は多対多の関係で、書籍は1名以上の著者を必ず保持する。


用語定義(ユビキタス言語)

以降の説明で言葉の意味がぶれないように、主要な用語を明確化します。モデル名とAPIの語彙を揃えると、読み手の誤解を減らせます。

用語説明
Author著者エンティティ
Book書籍エンティティ
AuthorId著者ID(値オブジェクト)
BookId書籍ID(値オブジェクト)
Status出版状況(未出版/出版済み)

ユースケース

ユーザーが何をしたいかを短く列挙します。ここでの範囲がAPIとテーブル設計の境界になります。更新・削除は仕様に記載がないため対象外です。

  • 著者を登録する/更新する
  • 書籍を登録する/更新する
  • 特定の著者に紐づく書籍一覧を取得する

ドメインモデル図

関係はシンプルです。著者1 : 書籍多の関係を基本とし、書籍は最低1名の著者を必須とします。

Author 1 --- * Book

この図は「どの情報がどこに属するか」を共有するための最小粒度の表現です。


エンティティ・値オブジェクト

不変条件を守りたい値(変わってはいけないルールを持つ値)はVO(値オブジェクトに、同一性で扱うもの(誰なのか、どれなのかを識別するもの)はエンティティに置きます。
この違いを意識して設計することで、モデルがより明確になり、扱いやすくなります。

Author

  • authorId: AuthorId
  • name: String
  • dateOfBirth: LocalDate(現在より過去のみ)

Book

  • bookId: BookId
  • title: String
  • price: Integer(0以上)
  • authors: List(1名以上必須)
  • status: Status(未出版/出版済み、出版済み→未出版への変更不可)

API仕様

ユースケースをそのままエンドポイントに落とし込むイメージです。パスや命名はユビキタス言語に合わせています。

メソッドパス機能
POST/api/authors著者の登録
PUT/api/authors/{id}著者の更新
POST/api/books書籍の登録(著者1名以上・価格0以上)
PUT/api/books/{id}書籍の更新
GET/api/authors/{id}/books著者に紐づく書籍一覧の取得

バリデーション・制約

ドメインの健全性を保つ最小限のルールに絞ります。エラー内容はAPI側で適切に返せるよう、チェック箇所をドメインに寄せるのが基本です。

  • 著者名:空文字は不可(100文字以内など)
  • 生年月日:未来日は不可(現在より過去のみ)
  • 書籍タイトル:空文字は不可
  • 価格:0以上
  • 著者リスト:1名以上必須
  • ステータス:出版済み → 未出版 への巻き戻しは禁止

テーブル設計

著者と書籍は多対多(1冊に複数著者/1人が複数冊)なので、中間テーブルで結びます。DBでは必要最低限の制約だけを持たせます。

authors(著者)

カラム名制約
author_idBIGINTPK
nameVARCHAR(100)NOT NULL
date_of_birthDATENOT NULL

最小制約

  • nameは必須
  • date_of_birthは過去日

books(書籍)

カラム名制約
book_idBIGINTPK
titleVARCHAR(255)NOT NULL
priceINTEGERNOT NULL(0以上)
statusVARCHAR(20)NOT NULL(列挙型)

最小制約

  • titleは必須
  • price >= 0
  • statusは許可値のみ(未出版、出版済み)

books_authors(中間)

カラム名制約
book_idBIGINTFK(books)
author_idBIGINTFK(authors)

最小制約

  • 両カラムは既存レコードを参照(外部キー必須)
  • (book_id, author_id) を一意(重複紐付け防止)

テスト方針

以下の内容でテストを作成します(名称は統一して「ドメイン」「API」「DB」)。

ドメイン(単体)

  • Book
    • price >= 0 を満たさないとエラー
    • titleが空だとエラー
    • authorsが1名未満(空配列)だとエラー
    • statusはUNPUBLISHED → PUBLISHEDのみ許可(PUBLISHED → UNPUBLISHEDは不可
  • Author
    • nameが空だとエラー
    • date_of_birthは現在より過去でないとエラー

API(最小経路)

  • 正常系
    • POST /api/authors で著者を1件作成
    • POST /api/books で上記著者IDを含めて書籍を作成(price >= 0、status = UNPUBLISHED)
    • GET /api/authors/{id}/books で当該書籍が取得できること
    • PUT /api/books/{id}UNPUBLISHED → PUBLISHED に更新できること
  • 異常系(代表)
    • POST /api/booksauthors 空/price < 0title 空 → 400系
    • POST /api/books存在しない authorId を指定 → 400/409系
    • PUT /api/books/{id}PUBLISHED → UNPUBLISHED を試みる → 400/409系

DB

  • 外部キー違反で挿入が失敗すること
    (books_authors.author_id に存在しないIDを入れるなど)
  • 一意制約:books_authors (book_id, author_id) の重複挿入が失敗すること

まとめ

記事に示された機能要件(登録・更新/著者→書籍の取得)、および 属性制約(価格0以上、著者1名以上、出版済みの巻き戻し不可、生年月日は過去) に一致する設計を、DDDに沿って整理しました。この記事がDDDを勉強している方の参考になれば幸いです。

参考リンク

コメント

タイトルとURLをコピーしました