こんにちは、リファクタリング大好きなミノ駆動です。
株式会社スタメンでは、企業エンゲージメント構築サービスTUNAG(ツナグ)の技術的負債解消と今後の持続的成長のため、ドメイン駆動設計(DDD)の導入を検討しています。
ところでDDDはとかく理解しづらく、何のためのDDDなんだという議論になりがちです。この記事では、DDDの真の主人公コアドメインを中心に、DDDが何を解決するものなのか、全体像を改めて整理します。
この記事で扱う内容
DDDが解決したい課題と解決方法の全体像。
この記事では扱わない内容
設計パターンの実例などの実装詳細。
大事な前提 〜利益を得るためのサービス開発
会社でのサービス開発は、趣味や道楽でやるものでしょうか。違いますね。ビジネスとして、企業活動としてサービス開発しています。当たり前の話ですが、利益を得られるように開発しなければなりません。
ドメイン駆動設計は、継続的に利益向上するための設計手法であり思想だと考えます。理由を以下に説明します。
DDDが解決したい課題
DDDが解決したい課題は、大きくは以下2つです。
- 顧客満足度低下による利益減:「今作ってるもの、本当に顧客に喜ぶものなの…?」と疑問に感じながらの開発が常態化。そしてやっぱり顧客にウケない。顧客離れが起き、稼げなくなる。
- 開発コスト増大による利益減:「この機能を実装したら顧客にウケることは間違いないんだ、でも技術的負債があまりにも酷すぎて機能追加が難しすぎる!」……開発生産性が悪化し、リリースまでの時間やコストが増大。折角の利益が膨大な開発コストによって相殺されてしまう。
DDDが目指す世界
上記課題が解決された世界は次のようになるでしょう。
- 機能性向上:サービスの競争優位性がチームに理解されており、競争優位性を高め続けられている状態。継続的に利益が増大し続けている状態。
- 変更容易性向上:技術的負債に苦しめられることがなく、いつでも迅速に機能を実装しリリースし続けている状態。
「ドメイン」とは
まずこの記事を理解する上でドメインという概念の理解が必要です。
世の中には解決が必要な問題がいろいろあります。「楽しい時間を過ごしたい」「美味しいものを食べたい」「新しい服がほしい」など、いわゆる人の欲求ですね。そうしたいろんな問題の中で、特定の範囲の問題を問題領域と呼びます。
次の図における楕円が問題領域の一例です。ここでの問題領域は「楽しい時間を過ごしたい」とします。こうした問題領域のことをドメインと呼びます。
さて問題に対し、世の中にはさまざまな解決方法が用意されています。この「楽しい時間を過ごしたい」という問題領域には、例えば世の中にはスポーツ観戦や旅行、ゲームなどがあります。こうしたものを、問題領域に対して解決領域と呼びます。
問題領域の中にさらに個々の問題領域がある
次の図はX(旧Twitter)の例です。
「コンテンツを楽しみたい」という全体の大きな問題に対応する解決領域がXだと仮定します。Xには、さらに個々の問題領域があると考えられます。「話題性のあるコンテンツを見たい」「情報収集したい」「TLに流れてくるコンテンツを健全に保ちたい」…。こうした個々の問題領域に対して、Xでは上図の赤四角で示すような機能が用意されています。
どこに開発リソースを投じるか?
お金は無限にありません。有限です。会社のお金も有限です。当然開発予算も有限です。限りある予算の中で、顧客が価値を感じるサービスを作っていかなければなりません。
上で説明したように、サービスには対応する問題領域がいろいろあります。その中でどの領域に投資しますか?
選択と集中 費用対効果を高める
特定の事業分野に経営資源を集中することを選択と集中といいます。経営戦略上、自社が得意とする、差別化可能な、競争優位性のある分野に集中投資することで費用対効果を高めます。
サービス開発も同様に選択と集中が必要です。不得意な、競争優位性のない分野に開発コストをかけていいものでしょうか…? サービスの魅力が大して高まらないでしょうし、その間にも競合他社に出し抜かれるでしょう。
皆さんも思い浮かべてください。自社サービスにおける、差別化可能な競争優位性のある分野は何でしょうか…?? それがコアドメインです。
中心的事業領域 コアドメイン
コアドメインはDDDでいの一番で登場する、超重要概念です。DDD序文からの引用です。
DDDを駆動している原則は次の3つだけです。
・コアドメインに集中すること。
・ドメインの実践者とソフトウェアの実践者による創造的な共同作業を通じて、モデルを探究すること。
・明示的な境界づけられたコンテキストの内部で、ユビキタス言語を語ること。
コアドメインとは中心的価値を発揮する事業領域のことです。競争優位性があり、他社との差別化が図られます。「その部分がなくなると商売が成り立たなくなってしまう」ぐらい重要な領域です。
コアドメインとは、いわゆる商品の「ウリ」の部分です。昔から営業さんは「うちの商品は◯◯がウリなんですよー」と営業をかけていましたよね。
コアの例
他社さんのコアと思わしき分かりやすい例です( ※あくまで私個人の見解です )。
ハッピーターン:亀田製菓さんのお菓子。粉(ハッピーパウダー)がコアでしょう。粉250%増量タイプも販売されています。
スプラトゥーン:任天堂さんのインクバシャバシャゲーム。インクがコアでしょう。陣地確保、移動経路開拓、高速移動、攻撃、補給、隠匿などあらゆる役割をインクが担っています。インクを中心にゲームのあらゆる要素がデザインされています。
コンビニやECサイトは、皆さん普段から利用されていると思います。商品を見たとき、「この商品の競争優位性は何だろう、何を差別化してるんだろう、何をウリにしているんだろう」と考えてみるのが良いでしょう。
コアドメインの価値を高められない!
さて、ソフトウェアではない話が多少混じりましたが、ソフトウェアの話に戻ります。サービス開発では、コアドメインの価値を高めようにも高められない、価値向上を妨害するさまざまな要因があります。
◆妨害要因1 : そもそもコアドメインが何か分からない
そもそも自分たちのサービスのコアドメインが分からない、何が競争優位性なのか分からないといった状況です。
スタートアップ直後は何をやりたいのか明確ですが、サービスが大きくなってくると何が自分たちの強みなのか分からなくなってくる場合がよくあります。こうなると、その場の思いつきで五月雨式に機能が実装されがちになります。顧客のニーズをなかなか満たせず、競争優位性が育ちにくくなり、サービスの価値は低減する一方になります。
◆妨害要因2 : サブドメインのロジックとの混在、密結合
コアドメイン以外の問題領域をサブドメインと呼びます。サブドメインの分かりやすい例は決済。多くのECサイトには決済機能があります。決済の不具合は重大なインシデントとなるので、決済は慎重に作り込む必要があります。では決済はコアドメインか? といえばそうではなく、どのECサイトにも実装されているもので、決済自体はサービスの競争優位性とはなりません。
ところで現実のサービスでは、サブドメインのロジックとコアドメインのロジック同士が混在したり、密結合になっている、ということが珍しくありません。このような構造では、コアとサブの見分けが難しくなったり、サブのロジックが邪魔でコアのロジックを上手く変更できない、といったことが発生します。
◆妨害要因3 : コアドメインロジックの散在、低凝集
コアドメインのロジックがソースコード全体のあちこちに散在し、低凝集に陥っているケースです。
コアのロジックを変更しようにもあちこちにバラバラに実装されているために、コアのロジックを探し回り影響範囲を調査するだけで莫大な時間がかかります。
◆妨害要因4 : DBロジックやViewロジックとの密結合
ドメインのロジックはアプリケーションロジックです。問題領域の問題を解決するルールや概念を実装したものです。以下がその例です。
- X(旧Twitter) : リツイートすると、フォロワーのタイムラインにリツイートしたツイートが反映される。
- スーパーマリオ:ダッシュしながらジャンプすると、高くジャンプできる。
- エクスプレス予約:新幹線の指定席は、乗車日の1ヶ月前から予約可能。
一方で、DBの責務は永続化です。Viewの責務は表示です。ドメインのルールとは関心事も責務も別です。ところがDBロジックやViewのロジックがドメインロジックと密結合になっているケースがあります。こうした密結合はコアドメインのロジック変更を困難にしてしまいます。
列挙したこれらの妨害要因があると、コアドメインの競争優位性を高めようにもなかなか高めることができません。あまりに酷いと商売が成り立たなくなります。
コアドメインの価値を高めるさまざまな手法
上記の妨害要因があるとコアドメインは「汚れている、汚染されている」状態になります。汚れているとは、
- コアドメインがサブドメインのロジックで汚れている
- コアドメインがDBロジックで汚れている
といった事象です。
こうした汚れを取り除き、綺麗に清潔に保つことで初めてコアドメインは価値向上の速度を上げることができます。汚れを取り除いたり、綺麗にしたり、コアドメインの価値を高めるさまざまな手法がドメイン駆動設計では解説されています。ドメイン駆動設計は、コアドメインの価値を高めるために考えられた設計思想であり設計手法と言えるでしょう。
◆手法1 : 境界付けられたコンテキスト
問題領域ごとにサブシステムに分割する考え方です(※より正確には、一枚岩モデルだとシステム構造が混乱してしまうため、文脈ごとに特化したモデルを設計し、各特化型モデルの適用可能範囲を定めたものが境界付けられたコンテキストです)。
手前味噌ですが、境界付けられたコンテキストについては下記動画とスライドが分かりやすいでしょう。
(※悲鳴が大音量で流れるのでご視聴には十分注意)
クソコード動画「一枚岩モデル」 #AWSDevDay pic.twitter.com/NwfOmXWy6F
— ミノ駆動 (@MinoDriven) 2022年11月9日
境界付けられたコンテキストの設計には何がコアで何がサブなのか事業分析が必要です。そしてコアドメインとサブドメインをシステムレベルで分離隔離します。そしてコアドメインの価値を簡潔に説明するドメインビジョン声明文を作成し、何がコアであるかを明示します。
こうすることで、以下の利点が生じます。
- 何がコアで何がサブか、システム構造レベルで見分けがつくようになる。どのロジックが競争優位性を発揮するのか理解できるようになる。
- コアがコアロジックだけの純粋な高凝集になり、サブと疎結合になる。
これらのメリットにより、上で挙げた妨害要因1〜3が解消されるでしょう。
◆手法2 : 階層型アーキテクチャ
ドメイン関心事や技術関心事など、関心事単位で階層ごとに分離します。次の図はクリーンアーキテクチャの思想でドメイン層を他の関心事から分離したアーキテクチャ図です。
%%{ init: { 'theme': 'base', 'themeVariables': { 'background': '#000000', 'primaryTextColor': '#6360DC' } } }%% graph TD subgraph A["外界"] View層 DB end subgraph B["入出力変換"] Controller層 Infrastructure層 end subgraph C["ユースケース解決"] UseCase層 end subgraph D["ドメインルール解決"] Domain層 end View層 --- Controller層 Controller層 --- UseCase層 DB --- Infrastructure層 Infrastructure層 --- UseCase層 UseCase層 --- Domain層 Infrastructure層 --- Domain層
例えばDBやファイルなどの永続化に関するロジックは、Infrastructure層にてRepository interfaceの実装クラスにカプセル化します。Repositoryは永続化知識のカプセル化に用いられるパターンです(※正確にはオブジェクトのコレクションをエミュレートする仕組みであり、DBやファイルは仕組みの一形態ににすぎない)。
このように隔離することで、コアロジック(上図のDomain層)が他のロジックに汚されずに済みます。これで妨害要因4が解消されます。
◆手法3 : Entity, ValueObject, Aggregateなどの設計パターン
上記手法2まで説明した考え方により、コアドメインのロジックを隔離できました。
さて、隔離できたはいいものの、コアドメインのロジックが複雑化し技術的負債になってしまっては開発生産性を出せません。負債化しないよう、適切な関心事単位で疎結合高凝集にします。そこで登場するのがドメイン駆動設計に登場するValueObject、Entity、Aggregateなどの設計パターンです。ここまで来てようやっと見覚えのあるものが登場するんですね。
%%{ init: { 'theme': 'base', 'themeVariables': { 'background': '#000000', 'primaryTextColor': '#6360DC' } } }%% classDiagram namespace Core { class Repository { <<interface>> } class Aggregate class Entity class ValueObject } Repository ..> Aggregate Aggregate --> Entity Aggregate --> ValueObject Entity --> ValueObject
これらのパターンを駆使し、変更容易性が向上するよう内部構造を整理していきます。また、ここに挙げた以外にも役立つ設計があるならどんどん使って良い、というのがドメイン駆動設計の見解です。
ここまで紹介した手法1〜3、コアの邪魔になるものをひたすら分離隔離していくための手法であることが理解できると思います。
◆手法4 : 蒸留、深いモデル
ここまでの手法は主にコアドメインの技術的負債を解消し、変更容易性(ソフトウェア品質特性の一種、なるべくバグを埋め込まずどれだけ素早く正確に変更できるかを示す度合い)を向上させる観点の設計技法です。しかしDDDはこれだけではありません。
DDDではリファクタリングを繰り返し、ドメインモデルを改良することの重要性を説いています。なぜでしょうか。改良を繰り返していくと「このドメインの問題を解決するには、実はこういう構造のモデルが相応しいんじゃないか!?」と気付く瞬間が訪れます。それが深いモデルです。
深いモデルとは、深い理解に基づいた本質を表現するモデルとDDDでは説明されます。深いモデルにより機能性(ソフトウェア品質特性の一種、顧客ニーズをどれだけ満たすかを示す度合い)の向上が期待できます。深いモデルは一朝一夕に発見または設計できるものではありません。重要なところに焦点を当てて改良を繰り返す、蒸留というプロセスを経る必要があります。
蒸留には、ドメインの深い理解の獲得が必要なので、ドメインすなわち事業課題やその領域のオキテに詳しい人(ドメインエキスパート)との対話が必要です。そして事業内容を理解するためにもユビキタス言語の策定が必要です。ユビキタス言語とは、ステークホルダー全員が理解可能な言葉であり、境界付けられたコンテキストごとに意味が一意に定まる言葉です(文脈によって言葉の意味が違うので)。ユビキタス言語は単に「全員が理解できる言葉」ではなく、目的を表現する言葉です。DDDの「意図の明白なインターフェース」では次のように説明しています。
クラスと操作には、その効果と目的を記述する名前を付け、約束したことを実行する手段には言及しないこと。(中略)こうした名前はユビキタス言語に従っていなければならない。(中略)そうしたインターフェースには、手段ではなく目的の観点から語らせなければならない。
(『エリック・エヴァンスのドメイン駆動設計』p.252より引用)
この説明から、ユビキタス言語が目的を表現する言葉であることが分かります。そもそもシステムは何らかの目的を達成するために開発される手段であることから考えても不自然ではありません(※ちなみに拙著『良いコード/悪いコードで学ぶ設計入門』では「目的駆動名前設計」と題してユビキタス言語の操り方を解説しています)。
目的ベースでユビキタス言語を策定し、目的に着目しやすくなることで良い効果が生まれます。「この目的を達成するためならば、今の実装や構造にこだわる必要はないよね。もっと良い手段があるのでは!?」という着想が得られやすくなります。そしてそれが深いモデルの発見や設計に繋がる、と考えることができます。
これは馬車しかなかった時代の自動車のイノベーション話に似ています。「もっと早い馬がほしい」と顧客に言われて馬の肉体構造や馬車の構造を一生懸命分析しても、自動車は生み出されません。現行手段に囚われず、顧客課題と真の目的に向き合い、目的をより満たすものを考え出す姿勢が大事です。
DDDで解決したいこと まとめ
以上、DDDで解決したい課題と、解決手法の全体像を紹介しました。要約すると以下となります。
- DDDは、中心的事業領域であるコアドメインの価値を高め、継続的に利益向上していくための設計手法。
- DDDは、コアドメインの機能性と変更容易性の向上に貢献する。
- 境界付けられたコンテキストにより、コアドメインとサブドメインを疎結合にする。
- 階層型アーキテクチャにより、Domain層とそれ以外の技術層を疎結合にする。
- Entity、ValueObject、AggregateなどのパターンによりDomain層内をドメイン概念レベルで高凝集疎結合にし、変更容易性を向上させる。
- 蒸留プロセスにより繰り返しモデルを改良し、機能性向上に貢献する深いモデルを発見・設計する。
DDDの向き不向き
解説したDDDの特徴を踏まえると、DDDには向き不向きがあることが分かります。
DDDは、以下を満たすものに向いていると言えるでしょう。
- 事業の主力、競争優位性の発揮対象
- 長期開発対象
- 複雑な事業課題を取り扱うもの
DDDは、以下には不向きでしょう。設計コストが高くつきます。
- サブやオプション的な位置付けのサービスや機能
- プロトタイプなど短期開発で終わるもの
- 取り扱う事業課題が単純で、CRUD程度のシンプルな作りになるもの
おまけ 実はあなたもDDDを実践している?
「そうは言ってもDDDの実践は結構大変そうだなぁ」と感じた方がおられるかもしれません。しかし一方で、私たちは日々の生活でDDDを実践していると私は考えます。
人には皆それぞれ人生のコアドメインがあります。人はそれを生きがいと呼びます。大好きな趣味や、大切な人との時間、推しのゲーム……それぞれあることでしょう。生きがいは神聖不可侵であり、なんぴとたりとも邪魔は許されません。例えば自分の趣味の大規模イベントや大事な旅行の予定があるとき、アクシデントが発生したり別の用事が突然割り込まれたりすると台無しになってしまいます。台無しにならぬよう健康に気をつけたり、仕事をしっかり片付けて割り込みが入らないようにした経験、皆さんもあると思います。
ちなみに私はフロムソフトウェアさんのゲーム『アーマード・コア6』の発売に備えるために万全の体制で臨みました(下記ツイートは引用)。
今日も1日#アーマードコア6 #AC6 pic.twitter.com/7069RC9Ulx
— 魚遣(うおつか) (@nou_ni_hitomi) 2023年8月22日
生きがいの大切な時間を守るために、邪魔するものを徹底的に除去する。ソフトウェア開発も同様に、競争優位性を発揮するコアドメインを守るために、コア以外の要素を徹底的に分離隔離する、これがドメイン駆動設計なのです。
おわりに
以上の解説のもと、DDDを活用してプロダクトの価値をより高めるべく邁進していきます。
弊社スタメンはTUNAGを中心に右肩上がりの成長を続けています。この成長をより高めるため、私に課せられた責務は非常に重要なものになっております。
この勢いで弊社スタメンはさらなる事業拡大を目指し、採用活動をより活性化していきます。現在従業員数は約80名。今の規模の2倍、3倍、5倍とスケールさせていきます。
エンジニア絶賛募集中です。私と一緒に働いてみたい、興味がある方は、ぜひ下記にアクセスしてみてください。