■ 1. 概要
- シンプルなランキング表示APIにDDDを適用した個人開発の事例を通じ「なぜ詰め替えが多いのか」という疑問から得た気づきを共有する記事
■ 2. 実装内容
- 要件:
- 入力: ランキングの開始位置(start)
- 出力: ユーザーID・ユーザー名・スコア・順位
- ロジック: Userテーブルからスコア降順で取得しstartをオフセットとして順位を付ける
- DDDのリッチドメインモデルスタイルを採用しRankingEntry・Ranking・EntryWithPositionのエンティティを定義した
■ 3. 詰め替えへの疑問
- データの流れに対して詰め替えが多すぎると感じた
- DBモデル → RankingEntry → Ranking → EntryWithPosition → レスポンスDTO
- DDDでは外部から取得した値をprivateにしてゲッターで公開し不正な値がユースケースに流れ込まないようにするという考え方がある
- RankingEntryのフィールドがUserテーブルのカラムとほぼ一致しておりドメインがDBスキーマに依存しているように見えた
■ 4. 結論
- ドメインはDBスキーマを知らないのではなく扱っていたドメインが非常にシンプルだったことが混乱の原因だった
- DDDが活きる条件:
- 複数のリポジトリから異質なデータを集約し複雑なビジネスロジックで価値を生むケース(例: 決済システムの詐欺判定ドメイン)
- 今回のケースの特徴:
- 単一テーブルからのデータ取得でロジックはforループによる順位付けのみ
- ドメインが複数の関心事を統合して価値を生む複雑さを持っていなかった
- アネミックモデルにした場合フィールドがpublicなためコンストラクタを経由せずに不正な値を入れられる問題があり守るべき不変条件がないなら実質的にただの構造体と変わらない
- 複雑なドメインロジックがない場合はエンティティへの詰め替えよりシンプルな構造体(DTO)として扱う方が見通しが良い
■ 5. まとめ
- DDDが活きるケースと今回のケースの比較:
- データソース: DDDは複数リポジトリの統合 / 今回は単一テーブル
- ロジック: DDDは複雑なビジネスルール / 今回はforループによる順位付け
- ドメインとテーブルの関係: DDDは異なる概念 / 今回はほぼ一致
- 詰め替えのコスト: DDDは払う価値がある / 今回はコストが目的を超える
- パターンを適用する目的を忘れシンプルな問題を複雑にしてしまうことを避けるためには「なぜこれを適用するのか」を考え続けることが重要