ページ一覧を開く

フットサルNOW DDDケーススタディ

CASE STUDY 01

フットサルNOWでDDDを検証する

ドメインモデル検証中

募集・参加申請・承認を題材に、ガイドラインの推奨が実装でも機能するか確かめます。完成例として見せるのではなく、実装で分かったことと未検証の仮説を区別して記録します。

画面プロトタイプを見る →5つのDomain Testが実行可能

START HERE · ドメインを理解する

参加申請から承認までの具体例を使って、参加募集の業務を理解する

DDDに「必ずこの作業から始める」という固定手順はありません。フットサルNOWでは、まず参加募集を題材に、利用者と開発者が具体例を会話します。その過程で、業務の言葉、判断、ルール、まだ分からないことを見つけ、理解に合わせてドメインモデルとコードを繰り返し更新します。

この機能で実現したいこと

今日フットサルをしたい人が、参加可能な募集を見つけ、主催者の承認を得られるようにする。

最初に扱うシナリオ

参加者が募集中の募集へ申請し、主催者が定員を確認して承認する。

今回は後回しにする

募集作成、決済、チャット、Push通知、プロフィール評価、会場予約。

最初に会話する一つの流れ

1

募集を見つける

2

参加申請する

3

主催者が承認する

4

参加が確定する

各矢印で「誰が何を判断するか」「失敗するとしたら何か」を質問します。

正常例・例外・未決定を並べる

正常例

定員2人の募集へplayer-1が申請し、承認される。残り枠は1人になる。

例外として確認

  • ・主催者自身の応募
  • ・同じ人の二重応募
  • ・定員超過の承認
  • ・期限後の応募

まだ答えがない問い

  • ・却下後に再応募できる?
  • ・承認後の辞退は?
  • ・最後の1枠を同時承認したら?

ここまでで明確にすること

  1. 01目的を表す一文
  2. 02一つの利用シナリオ
  3. 03正常例・例外・未決定の問い
  4. 04チームで揃えた業務の言葉

時間では区切りません。この4つをチームで共有できたら、クラス図やディレクトリ構成の検討へ進みます。

STEP 01 · LANGUAGE

まず、画面ではなく業務の言葉を揃える

画面上の「参加する」は即時参加ではありません。主催者の承認前は「参加申請」であり、申請中・承認・却下を区別します。この違いをドメインモデルの名前と状態に反映しました。

募集Recruitment主催者が日時・期限・定員を決め、参加者を募る単位
参加申請RecruitmentApplication参加希望者が募集に対して行う申請
承認approve主催者が申請を参加確定へ進める操作
残り枠remainingCapacity定員から承認済み人数を引いた値

STEP 02 · MODEL

なぜRecruitmentをAggregate Rootにしたか

応募可否と承認可否は、参加申請だけを見ても判断できません。募集状態、開催日時、応募期限、主催者、既存申請、定員を同時に確認するため、Recruitmentを変更の入口にしました。

Application Service

応募・承認Use Case

RECRUITMENT AGGREGATE

Recruitment

状態・期限・主催者・定員を保持

↓ Rootだけが追加・変更
RecruitmentApplication
RecruitmentRuleError
Application Serviceは内部配列を直接変更せず、apply・approve・reject・closeという業務操作を呼びます。

応募時

受付中か、期限内か、本人が主催者でないか、二重応募でないか

承認時

受付中か、開催前か、定員内か、申請中か

変更後

残り枠は承認済み人数から計算し、外部から直接書き換えない

STEP 03 · VERIFY

業務ルールをテストまで追跡する

「守るべき」と書くだけではなく、どの操作が所有し、どのテストが保証しているかを対応づけます。現在は次の5ルールをDomain Testで確認しています。

1参加申請は申請中で始まるRecruitment.apply()Test: 参加申請をPendingで受け付ける
2主催者は自分の募集へ応募できないRecruitment.apply()Test: 主催者自身の応募を拒否する
3同じ人は二重応募できないRecruitment.apply()Test: 同じユーザーの二重応募を拒否する
4定員を超えて承認できないRecruitment.approve()Test: 定員を超える承認を拒否する
5申請中以外は再承認できないRecruitment.approve()Test: Pending以外の申請は再承認できない

Domain: src/features/futsal/domain/Recruitment.ts

Test: src/features/futsal/domain/Recruitment.test.ts

npm run test:futsal

CURRENT GAP

画面とドメインモデルは、まだ接続されていない

現在の画面は操作感を確認するプロトタイプです。「この募集に申請する」を押すとReactのローカル状態へIDを追加しますが、Recruitment.apply()は呼んでいません。

実装・検証済み

  • ✓ Recruitmentの状態と操作
  • ✓ 5つの不変条件テスト
  • ✓ 業務エラーの識別コード

未実装・未検証

  • ○ Application Service
  • ○ Repositoryと永続化
  • ○ 画面からDomainへの接続
  • ○ 同時更新と通知

NEXT EXPERIMENT

次の実装で確かめること

  1. 1

    画面をUse Caseへ接続

    ReactのappliedIdsをやめ、ApplyToRecruitmentがRecruitmentを取得・操作・保存する。

  2. 2

    Repository境界を検証

    interfaceを利用側へ置き、まずInMemory実装でUse Caseをテストする。

  3. 3

    同時承認を検証

    最後の1枠を同時に承認した場合に、VersionやDB制約で定員を守れるか確認する。

  4. 4

    通知をイベントで分離

    承認後通知が本当にDomain Eventを必要とするか、失敗時の再送を含めて試す。

各実装で「推奨どおりに置けたか」「例外が必要だったか」「テストしやすくなったか」を記録し、判断ルールの推奨・例外・Good / Bad Exampleを更新します。