techvit
← Blog
EngineeringArchitectureDDDCQRSPostgreSQL

DDDやClean Architectureの導入で、RDBの強みを失っていないか

Webシステムのアーキテクチャを考えるとき、最近気になっていることがある。

RDBが得意としていることを設計上の理由から制限し、その結果として発生した問題を、さらに高度なアーキテクチャで解決しようとしていないか。

DDD、Clean Architecture、CQRSはどれも有効な設計手法だ。ただし、適用する順番や対象を間違えると、システムを良くするための設計が、かえってシステムを複雑にする。

RDBは単なる「データの保存場所」ではない

PostgreSQLやMySQLには、長年磨かれてきた強力な機能がある。JOIN、INDEX、トランザクション、制約、クエリオプティマイザ、集計、ロックと分離レベル。一般的なWebシステムなら、

HTTP Request → Application → PostgreSQL

というシンプルな構成でも相当な規模まで対応できる。

一方、Clean Architectureの原則を厳格に適用すると、こうなる。

Controller → UseCase → Repository Interface
  → Repository Implementation → ORM → Database

この構造自体に問題はない。責務の分離やテスト容易性のメリットは十分ある。問題になるのは、「RepositoryはAggregate単位にする」「Aggregateをまたいだ処理は避ける」「DBはInfrastructure層の詳細として扱う」といった原則を、システムの性質を考えずに参照系まで一律に適用してしまう場合だ。

自分たちでパフォーマンス問題を作っていないか

注文一覧画面を考える。画面には注文情報に加えて、顧客情報・商品情報・支払い状況・配送状況を表示するとする。RDBなら必要なテーブルをJOINして一度に取得できる。しかしAggregate境界を厳格に守ると、

注文一覧を取得
  → OrderRepository
  → CustomerRepository
  → ProductRepository
  → ShippingRepository

という処理になり、N+1問題や大量のクエリ発行につながる。すると「Read側が遅いのでRead Modelを作ろう」という話になり、

Read Modelを作る → CQRSを導入する → Read DBを分離する
  → イベントでデータを同期する → Event Busが必要になる
  → 結果整合性を考慮する必要が出てくる

と、複雑性が急激に高まっていく。それだけの規模や要件があるなら合理的な選択だ。しかしその前に一度考えたい。

これは、最初から適切なJOINを書けば解決できた問題ではないか。

なお、DDDの文献自体も参照系にはRepositoryを経由しないクエリサービスを認めている。「一覧画面もAggregate経由で組み立てる」のは、DDDの要請というよりDDDの過剰適用であることが多い。

CQRSが悪いわけではない

CQRSそのものを否定しているわけではない。書き込み側には「注文 → 在庫確保 → 決済 → 配送手配」という複雑な業務処理があり、読み取り側にはダッシュボード・分析・検索・レポートというまったく異なる要求がある。そういうシステムなら、Write ModelとRead Modelを分離する合理性がある。読み書きのアクセス量が大きく異なる場合や、検索だけElasticsearchに任せたい場合もそうだ。

また、CQRSは本来「読み書きのモデルを分ける」ことであって、Read DBの分離やEvent Busまでが必須なわけではない。同じDBに対して参照専用のクエリを書くだけの軽量なCQRSで足りることも多い。

重要なのは「CQRSを使えるかどうか」ではなく、この問題を解決するためにCQRSが本当に必要なのかを考えることだ。

まずはRDBをきちんと使う

CRUD中心のWebサービスなら、アーキテクチャを変える前にRDB側で改善できないかを確認する。

  1. 実際に発行されているSQLを確認する
  2. EXPLAIN ANALYZEで実行計画を見る
  3. INDEXとJOINを見直す
  4. N+1を解消する
  5. Connection Poolを確認する
  6. 必要ならCacheを導入する
  7. それでも要件を満たせないなら、アーキテクチャを再検討する

CQRSの検討はその後でも遅くない。DBMSはクエリ最適化・並行制御・整合性について何十年も研究と改善が続けられてきた巨大なソフトウェアであり、その能力を使わずにアプリケーション側ですべて解決しようとするのは、良い設計とは限らない。

「DBに依存しない」と「DBの能力を使わない」は違う

Clean ArchitectureがDBをInfrastructureの詳細として扱うのは、ビジネスロジックを特定のORMやDB製品から分離するためであって、DBの機能を避けるためではない。PostgreSQLを採用しておきながらJOINも制約もDBに任せず、同じ仕組みをApplication層で再現しているなら、それは疎結合ではなく、強力な道具を使えていないだけだ。抽象化によって下位レイヤーの能力まで失っては本末転倒になる。

アーキテクチャは目的ではなく手段

高度なアーキテクチャを採用していることと、高度な設計ができていることは同じではない。設計で本当に難しいのは、「このシステムにそこまで複雑な仕組みは必要ない」と判断することの方だ。必要になったときに導入できる知識を持ちながら、必要になるまでシンプルな構成を維持する。それも設計能力だと思う。

この問題は、AIによるコーディングが一般的になって、さらに重要になっている。Repository、UseCase、DTO、Mapper、Domain Event、Read Model — AIはこうしたコードを数分で生成できる。以前は「実装工数が大きい」ことが自然なブレーキになっていたが、そのブレーキは消えつつある。

しかし、コードを書くコストが下がっても、複雑性を維持するコストは下がっていない。 生成されたコードも、誰かが仕様を理解し、テストし、障害時にデバッグし、数年後に変更することになる。だからこそ「このコードはそもそも必要なのか」と考えることが、以前より重要になる。

複雑なアーキテクチャを理解した上で、あえて使わない判断ができること。RDBで解決できる問題はRDBに任せ、アプリケーションで解決すべき問題はアプリケーションで解決すること。その境界を適切に判断できることまで含めて、エンジニアの設計力なのだと思う。

関連するService

このページを共有

XはてブLINEFacebook