貧血症は「怠慢」ではなく「構造的必然」である
多くのエンジニアが、DDD(ドメイン駆動設計)を導入しようとして、結局は「getterとsetterだけのクラス」と「肥大化したサービス層」という、いわゆるドメインモデル貧血症に陥る経験をしているはずだ。私も過去、幾度となくこの壁にぶつかってきた。なぜ、どれほど設計を練っても、ビジネスロジックがドメイン層から漏れ出し、ユースケース層に散らばってしまうのか。それは、多くのエンジニアが「怠慢」だからではない。むしろ、純粋性、性能、完全性という3つの要素を同時に満たすことができない「DDDトリレンマ」という構造的な制約に直面しているからに他ならない。
具体的に考えてみよう。小説投稿サービスの閲覧判定という、一見単純な機能を実装する際、我々は「公開範囲」や「公式作品フラグ」といった条件に直面する。ここで、フォロワー限定作品の判定が必要になった瞬間、DBへの問い合わせ(Repositoryの呼び出し)が発生する。この時、エンジニアは以下の3つの選択肢を迫られることになる。
- A. ドメイン層がRepositoryを呼ぶ:依存の向きは逆転させても、戻り値にエラーやコンテキストが混入し、ドメインの純粋性が失われる。
- B. 先に全部取ってから渡す:ドメインは純粋に保たれるが、不要なクエリが大量に発行され、パフォーマンスが著しく低下する。
- C. 呼び出し側で分岐する:純粋性と性能は守れるが、ビジネスロジックがドメインの外に漏れ出し、コードの重複と仕様の乖離を招く。
現場のシニアエンジニアとして断言するが、このトリレンマにおいて、多くのチームが「C」を選択するのは極めて合理的な判断だ。しかし、それは「DDDの敗北」を意味する。ロジックが散逸したコードベースは、仕様変更のたびに複数の箇所を修正しなければならず、いずれデッドロックのような保守性の低下を招く。この「構造的な罠」を理解せず、単に「DDDが難しい」と片付けるのは、エンジニアとしてあまりに短絡的ではないだろうか。
Decisionパターンによる「判断」の型化
このトリレンマを解く鍵は、ロジックを「実行」するのではなく、「判断するために何が必要か」を型として定義することにある。テラーノベルのテックブログで紹介された「Decisionパターン」は、まさにこの本質を突いている。これは、ドメイン層の関数がbool値を返すのではなく、直和型(Goではinterfaceで代用)を用いて「判定終了」か「次のデータ取得要求」かを返すという設計手法だ。これにより、ドメイン層はDBやコンテキストを一切知ることなく、純粋なビジネスルールのみを記述できる。
このパターンの真骨頂は、呼び出し側(ユースケース層)が「対応表」に徹することができる点にある。ユースケース層は、ドメイン層から返された「ViewNeedFollowing(フォロー確認が必要)」といった状態を受け取り、Repositoryを叩いて結果をドメインに投げ返す。このループ構造により、ビジネスロジックのif文はすべてドメイン層に集約され、一方でインフラ層の都合(エラーハンドリングやクエリ発行)はユースケース層に分離される。これにより、純粋性、完全性、性能の3つを同時に満たすことが可能になる。
特筆すべきは、この設計が「事実の引き回し」を型で保証している点だ。判定の途中で必要なデータ(viewFacts)を構造体にまとめ、状態遷移とともに引き回すことで、必要なデータが欠落するリスクを排除している。これは、単なる設計パターンを超えた「型システムによる仕様の強制」であり、大規模な開発において仕様の不整合を防ぐ強力な武器となる。我々エンジニアは、コードを書く際に「どこで判断し、どこでデータを取得するか」という境界線を、もっと厳密に型で定義すべきではないだろうか。
明日から取り組むべき「設計の問い」
Decisionパターンは、DDDの戦術的パターンが「2003年当時のJavaの制約の産物」であるという批判に対する、現代的なGo言語による回答の一つと言える。しかし、この手法を導入すれば全てが解決するわけではない。重要なのは、このパターンが「判断の順序」をドメイン層に閉じ込めることで、仕様変更に対する耐性を劇的に高めているという点だ。例えば、VIP会員判定の後に購入履歴判定を行うといった順序の変更も、ドメイン層のDecideメソッドを修正するだけで完結する。これは、ユースケース層がビジネスの順序を知らなくて済むという、疎結合の極致である。
読者であるエンジニア諸君に問いたい。あなたの現在のコードベースにおいて、ビジネスロジックの「if文」はどこに存在しているだろうか。もしそれがユースケース層やAPI層に散らばっているなら、それは「貧血症」の初期症状かもしれない。Decisionパターンを導入することは、単にコードを綺麗にする作業ではない。それは、ビジネスのルールを「実行可能な仕様書」としてドメイン層に再定義する、極めて高度なエンジニアリングの営みである。
明日から、まずは「判断のために何が必要か」を型で表現することから始めてみてほしい。Repositoryを呼ぶ前に、その判断が本当に今必要なのか、あるいは後回しにできるのかを型で表現するだけで、コードの質は劇的に変わるはずだ。我々が向き合うべきは、フレームワークの作法ではなく、ビジネスの複雑性をいかに型システムに落とし込み、保守可能な構造へと昇華させるかという、エンジニアとしての本質的な問いである。この「問い」を放棄した瞬間、我々のコードはスパゲッティ化への道を歩み始めることになるだろう。


コメント