simple-web-system technology

Webに関する技術をシンプルに扱うブログ

DDDの「理想」と「実用」の狭間で。あるいはGraphQLが救世主になる理由

最近、DDD(ドメイン駆動設計)について考えていると、どうしてもぶつかる壁がある。「参照系と更新系の非対称性」と、そこから生まれる「理想と現実の乖離」だ。

ドメインモデルは本来、ビジネスの世界をコードとして表現したいはずなのに、実用的なパフォーマンスを追い求めると、コード上の表現力がどんどん痩せ細っていく。この矛盾にどう折り合いをつけるか、最近の思考の整理。

理想:関係性の完全な表現

DDDを学び始めた頃、誰もが夢見るのが「Pureなドメインモデル」だ。 そこでは、すべてのオブジェクトが有機的に繋がっている。

// 理想的な世界
class Order {
  // 注文から顧客へ、オブジェクトとして参照を辿れる
  customer: Customer;
  lines: OrderLine[];
  
  // ロジックの中で自然に関係性を使える
  validate() {
    if (this.customer.isPremium()) { ... }
  }
}

これは「宣言的」で美しい。コードを見ればドメインの関係性が一目瞭然だからだ。しかし、これをRDBの上に実装しようとした瞬間、地獄を見る。

現実:ID参照という妥協

パフォーマンス、トランザクション境界、メモリ効率。これらを考慮した「実用的DDD」では、集約(Aggregate)の境界を厳密に切り、他への参照は「ID」で行うことが鉄則になる。

// 実用的な世界
class Order {
  // 参照を切る。あるのはIDだけ。
  customerId: CustomerId; 
  lines: OrderLine[];

  // 必要なデータはメソッド引数として渡してもらう
  validate(customerStatus: CustomerStatus) { ... }
}

これでパフォーマンス問題は解決する。しかし、代償として**「コードからドメインの地図が消える」**。 Order クラスを見ても、それが Customer とどういう関係なのか、直感的には分からなくなる。「ドメインをコードで表現する」と言いつつ、実態はIDの管理屋になっていないか?という虚無感が襲ってくる。

矛盾:宣言的でありたい vs 実用的でありたい

ここで生じているのは、以下の対立だ。

  1. 宣言的欲求(Ideal): ドメインの関係性や構造を、静的な型やコードとして「完全な形」で定義しておきたい。ドキュメントではなく、動く仕様として。
  2. 実用的制約(Pragmatic): 更新時のロック競合や無駄なRoadを防ぐために、モデルはバラバラに分断しておきたい。

「実用」を取ると「ドメインの全体像」が見えなくなり、「理想」を取ると「システム」が死ぬ。DDD自体が矛盾を抱えているようにさえ思える。

解決策:GraphQLを「理想のドメイン層」と見なす

この矛盾を解消するミッシングリンクとして、GraphQLが非常にハマるという結論に至った。

アーキテクチャのレイヤーを一つ増やし、役割を分担させるのだ。

  • バックエンド実装(更新系): 徹底的に「実用的DDD」に徹する。ID参照、塩漬けのデータ構造、小さな集約。ドライに徹する。
  • GraphQLスキーマ(インターフェース): ここに「理想的DDD」を記述する。

スキーマ=生きたドメイン定義書

バックエンドの実装では customerId しか持っていなくても、GraphQLのスキーマ上では堂々とリレーションを張る。

# ここに「理想」を書く
type Order {
  id: ID!
  # 実装上はIDしかなくても、スキーマではCustomerと繋がっていると宣言する
  customer: Customer! 
  lines: [OrderLine!]!
}

type Mutation {
  # CRUDではなく、ドメインの「振る舞い(ユースケース)」を定義する
  shipOrder(id: ID!): OrderPayload
}

こうすることで、以下のメリットが生まれる。

  1. 地図の復活: 開発者はスキーマschema.graphql)を見れば、ドメインの全容と関係性を理解できる。ドキュメントと違い、これは嘘をつかない(嘘なら動かないから)。
  2. 実装の隠蔽: Resolverが customerId を使ってよしなにデータを引いてくればいい(DataLoaderを使えばN+1も起きない)。裏側がどれだけ泥臭いID参照で最適化されていても、外からは「リッチなドメインモデル」に見える。
  3. CQRSの自然な導入: GraphQLはQuery(参照)とMutation(更新)が明確に分かれているため、DDDの推奨するCQRSと相性が良すぎる。

結論

ドメインの関係性をコードで表現したい」という欲求を、バックエンドのEntityクラス(実装詳細)だけで満たそうとするから辛くなる。

「関係性の表現(宣言)」はGraphQLスキーマに任せ、「整合性の確保(実用)」はバックエンドの集約に任せる。

まあ、そこそこ良い回答かなとは思う