2023/10/31

DDD(Domain Driven Design: ドメイン駆動設計)を布教したい

はじめに

今回はドメイン駆動設計を布教する際に押さえておくことを整理して記事にしたいと思います。

この記事では、DDDの概要を整理するとともに、DDDの採用におけるメリットを整理していきます。

これから勉強する人、そもそもDDDに興味がない人がすこしでもDDDに対して興味を持っていただければと思います。

DDDについて学習し、批判的な意見を持てるころには、設計への知見が深まっていると思います。

対象読者

  • バックエンドエンジニアで、設計の勉強をしてみたい人
  • DDDについてある程度調べたが採用メリットがいまいち理解できない人

まとめ

  • DDDは業務をモデリングする戦略的設計と、モデルを実装する場合のオブジェクト指向における実装パターンを指す戦術的設計に分けて語られる
  • DDDは何か特別な手法ではなく、業務の複雑さを愚直にソフトウェアに反映するための一つの考え方
  • DDDはデザインパターンのベストプラクティス集でもあり、クラス分割のための指針としても使える

過去の記事

ちなみに、過去にDDDについて学んだ際に書いた記事がいくつかあります。

「ドメイン駆動設計入門」を読む その1 ドメインオブジェクト編

「ドメイン駆動設計入門」を読む その2 ユースケースを組み立てるためのパターン編

LaravelにおけるRepositoryについて再考してみる

動機

DDDとクリーンアーキテクチャは設計思想として色物扱いされがちであったり、たびたび論争の火種になりがちです。
私の観測範囲だと批判のほとんどは理解不足や誤解によるところが大きいと感じています。
もちろんソフトウェア開発における銀の弾丸はないので、全てのソフトウェアに有効ではないですが、DDDについて理解しておくことで、ソフトウェア開発における設計の幅が広がると思います。
ただ、色物扱いされる理由でもありますが正しい情報が少ないのも事実です。そこでこの記事ではDDD入門として、参考となる情報源を紹介するとともに、採用におけるメリットを整理していきます。

DDDにおいて最も重要なこと

DDDは、関心事をモデリングし、分離することでソフトウェアの複雑性に向き合うことを目的としています。

DDDの二つの側面

DDDという思想は大きく分けると二つの側面を併せ持ちます。
1つは、戦略的設計、もう1つは戦術的設計と呼ばれます。

戦略的設計

こちらはドメイン、つまり業務の分析を行う際に、どのようにモデリングするかを考えることを中心としています。
どこまでが業務の範囲なのか、どのように分割するのかということをドメインエキスパートと呼ばれる業務に詳しい専門家とエンジニアが協力してモデリングを行っていく考え方です。

戦術的設計

こちらは戦略的設計で作成したモデルを実装する際に、どのように実装するかを考えることを中心としています。

例として

  • ValueObject
  • Entity
  • Repository
  • Service
  • Factory
  • Aggregate

などのパターンがあります。

これらは個別のデザインパターンとして知られているものもありますが、DDDの文脈として責務を分離するために用いられることが多いです。

個人的な理解としては設計における1種のフレームワークとして捉えています。

それぞれのパターンについてはいい記事が存在していたりするのでそちらに説明を譲ります。
DDD基礎解説:Entity、ValueObjectってなんなんだ

戦術的設計や軽量DDDと呼ばれたりするこちらのコードの品質に着目した考え方は、戦略的設計において改善されたモデルを継続的にソフトウェアに反映するために、拡張性や保守性を高めるためのベストプラクティス集と考えることができます。

軽量DDDには意味がない、というような意見を目にすることもありますが、私個人としては拡張性を高めるだけでも十分に価値があると考えています。

よくあるパターンとしては、戦略的設計、つまりモデリングをコードに反映したいと思った時にコード品質が低く、変更が容易でないなどの理由でコードがボトルネックとなってしまうパターンです。
このような場合は先にデザインパターンとしての軽量DDDをどんどん取り入れつつ、モデリングに徐々に着手していくことになると思います。
そもそもDDDは、継続してモデリングとソフトウェアへの反映を反復してソフトウェアの価値を高めることが大切なので、どちらが先か、ではなくどちらから始めてサイクルを回すかという違いでしかないと思っています。

DDDに適しているケース

DDDの目的はソフトウェアの「複雑さ」に立ち向かうことです。
つまり、複雑さをもつソフトウェアに適していると言えます。

ソフトウェアの抱える複雑さと辛さ

ここでは、業務で一定規模以上のソフトウェアを開発する際に抱える複雑さと辛さについて考えてみます。
つまり、どうして小難しいDDDなんてものを勉強して適用しなくちゃいけないの?という疑問に対する具体的な答えを考えてみようということです。

ちなみに一定規模というのはここではDBの持つテーブル数が20を超えたあたりからそれ以上の規模を想定しています。
プロダクトのフェーズでいうとある程度ユーザーがいて、今後もメンテナンスが必要な10-100のようなフェーズを想定します。

このフェーズのプロダクトに仕様変更や機能追加を行う場合、どういう手順を踏むでしょうか。

設計手法はどうであれ、DB設計は必要になると思います。

コードの設計についてはどうでしょうか。

このフェーズのプロダクトでは、設計したDBの構造がそのままCRUD機能として実装できることが少なくなってきたり、既存のDBのデータを使いたいが、レコードという単位ではないデータを扱う必要が出てきたりします。

このような場合にDDDではコードを作成する指標としてドメインモデルを作成します。

また、DDDにおいてはRepositoryパターンの採用によって具体的な永続化手段に依存せずにコードを記述することができます。このドメインではこういうモデルが必要、モデルを作成するためにDBから構築する処理はRepositoryにカプセル化しておく、というようなことができるため、ドメインモデルを中心としたコードの設計が可能になります。

参考事例: ドメイン駆動設計で保守性をあげたリニューアル事例 〜 ショッピングクーポンの設計紹介

DDDに適していないケース

DDDでは、ドメインに着目してクラスを設計するため、クラスの数としては単純なMVCと比較して多くなります。

そのため、単純なCRUD機能のみのプロダクトや、テーブル設計をそのままコードに落とし込むだけで十分足りてしまうようなプロダクトの場合にはメリットが薄くなります。
ソフトウェアにおいて、ある単位で整合性を保つ関係を表現するために、RDBが使用されます。RDBにおけるレコードというのはデータの永続化と整合性の担保を実現します。この仕組みが、ソフトウェアが大きく複雑になるに従って、ボトルネックになることがあります。データの永続化はしたいが、1つのデータが複数の領域において整合性を担保したいようなケースです。
このようなケースの解決策としてDDDが提案するのが、集約という考え方です。
DDDはこのようにソフトウェアとドメインの関連性に着目し、RDBのようなインフラと疎結合を実現することによって、ソフトウェアの複雑性に立ち向かいます。
そのため、RDBのレコードで整合性を担保できれば要件が十分に満たされるような規模のプロダクトでは、DDDのメリットは薄くなると言えると思います。

また、フレームワーク自体の思想と相反してしまう場合もあるため、使用するフレームワークやプロダクトとして重視することを整理して採用を判断することが大切です。

例えば、Railsのようなフレームワークは、MVCの思想を中心に設計されており、この思想の上で開発速度を向上することに重点を置いています。
そのため、DDDのような設計思想を採用することは、フレームワークの思想と相反してしまうため、採用することによって開発速度が低下する可能性があります。
Railsのメリットを活かしづらく、また型システムを持たないためレイヤードアーキテクチャの採用が難しく、DDDとしても中途半端な状態になりやすいため工夫しないと採用が難しいと思います。

Laravelの場合は、Railsに影響を受けたフレームワークですが、Railsほどのブラックボックスではなく、近年のPHPでは型システムのサポートも充実しているため、DDDを採用することで得られるメリットを享受しやすいと考えています。Laravelが優れている、というよりはRailsと比較すると異なる思想での開発を許容する文化があるフレームワークであると思っています。

DDDを既存のプロジェクトにどう導入するか

個人的には、戦術的設計から取り入れていくのが良いと考えています。
まずはコードベースで改善できる部分を改善していき、その過程でドメインモデルについての考察が深まり、その頃には改善における変更容易性のメリットを活かしてドメインモデルを洗練していくことができると思います。

まとめ

ソフトウェアというのは、時間の経過に従って新規作成より仕様変更のほうが工数がかかってしまうという状況が往々にして発生します。
プロダクトの成長に伴って、増加していく仕様変更の工数の増加を抑え、読みやすいコードを実現することはソフトウェアの価値を高めることにつながり、結果としてビジネスのスピードに追従できるソフトウェアを実現することにつながります。
DDDは、体系化されていてかつ採用実績の多い、ソフトウェアの設計思想のフレームワークともいえます。
個別のデザインパターンのメリットを活かしながらクラス分割するための指針としてまとまっており、ソフトウェア全体を通して一貫した方針でのクラス分割が可能になります。
まずは実装パターンのエッセンスを取り入れるところから、DDDの扉を開いてみてはいかがでしょうか。

参考記事

DDD基礎解説:Entity、ValueObjectってなんなんだ

ドメイン駆動設計で保守性をあげたリニューアル事例 〜 ショッピングクーポンの設計紹介