オブジェクトストレージ開発におけるDDD (ドメイン駆動設計)

この記事は、2022年7月27日(水)に行われた「さくらの夕べ Tech Night #5 Online」における発表を編集部にて記事化したものです。

はじめに

オブジェクトストレージというサービスの開発においてDDD(ドメイン駆動設計)をやってみたこと、実践してみたことについてお話ししていきたいと思います。

今日ご説明したいものとしては大きく3つあって、まず1つがDDDの基礎ですね。皆さんにDDDの目的とかメリットとか、そこら辺の知識を共通認識として持ちたいなと思っています。その後で、オブジェクトストレージにおいてどんなことをやって、どんなふうにDDDを進めていったのかを説明します。最後にまとめという流れで進めていきたいと思います。

DDDのよくある誤解

いきなりなんですが、皆さんはDDDとかドメイン駆動設計という言葉をインターネットで検索して、こんな書き込みを見たことはありませんか? 「エンジニアはドメインエキスパートの通訳者だ」とか「レイヤー構造を持ち込めばDDDになるんだよ」とか「DDDの構成要素をすべて利用しなければいけないんだ」とか「データの詰め替え作業がとてもつらい」とか、結構よく聞くものじゃないかなと思うんですけど、それに対する回答を持ち合わせてるんで、基礎を学びながら説明していきます。

DDDの基礎

これらの誤解に対して回答する前に、DDDの共通認識を持ちたいと思います。

ドメインとは何か

まずDDDの最初のDですね。ドメインなんですけど、これは何かって話で、いろんな書籍がありますけど、「エリック・エヴァンスのドメイン駆動設計」とかを参照すると、「ユーザの何らかの活動や関心や、プログラムを適用するそれらの対象領域」ってことで、いわゆる対象業務っていうふうに言われますね。それがドメインであると。

DDDとは何か

ではDDDとは何なのか、ドメイン駆動設計っていったい何なのかって話なんですけれど、ドメインエキスパートという、その業務をよく知っている人の頭の中にある知識を厳密に構成し抽象化したもの、それをドメインモデルって言うんですけど、そのドメインモデルをイテレーティブに深化する、つまりドメインモデルを何度も何度も繰り返して練度を上げていくことによって、ドメインの複雑さ、つまり業務の複雑さに立ち向かうとともにソフトウェアプロダクトを成功させるための、設計思想や哲学を指すと定義されています。

DDDの前提条件

そもそもDDDはアジャイルと結構密接に結びついていて、実は前提条件というのがあります。これは前述の書籍の序文に書かれていることなんですが、まずそもそも開発がイテレーティブであることが条件です。イテレーティブってのは、いわゆるCI/CDで繰り返し開発のサイクルが回っていることですね。もう1つは開発者とドメインエキスパートが密接に関わっていること。この2つが前提条件になります。

ドメインモデリングとは何か

ではドメインモデリングとは何かって話です。ドメインモデルとは業務領域、つまりいろんなドメインの考え方やとらえ方であると言いましたが、この現実の対象領域の概要を表現したものであって、そのストーリーを伝達、またはその意見を主張するものです。ちょっと難しく書いてますが、要するにドメインモデルを作り出すことですね。

実際にそれをどういうサイクルで動かしているかということですけど、まず対象領域があって、それに対して私達が領域を理解し、モデルを作り上げていくと。で、そのドメインモデルに基づくソフトウェアを実装することによってドメインの課題を解決していくと、そういう流れになっています。

ドメインモデルを用いる理由

ではなぜ私達がドメインモデルを作りたいのかっていうことなんですけど、その理由は主に3つあります。

まず1つが知識の蒸留です。これはドメインエキスパートと議論を重ね、そのドメインが抱えている問題の核心を突いたモデルを生成することで、ドメインをどのように考え、どのように関心を分離するかっていうのを明確にすることが可能になります。

2つ目がユビキタス言語の生成です。ドメインエキスパートやプロダクトに関わるステークホルダーが相互に理解できるような共通言語を用いて、ドメインモデルのセマンティクスを実装に反映することで、ドメインモデルによって相互にコミュニケーションができるようになります。そして、ドメインモデルを用いて相互にコミュニケーションを行うとともに、実際のドメインモデルはドキュメントとして著されることが多いため、ドキュメントと実装がきちんと結びつく状態になります。

そして3つ目は、2つ目に紐づきますけれども、実装の早期理解です。ドキュメントであるドメインモデルと実装がちゃんと結びついているんで、効率的な実装を支えるとともに、ドメインモデルについての理解や知識を抽象化できる、そういう利点があります。

「エンジニアはドメインエキスパートの通訳者だ」という誤解に対する回答

ここまで話をすると、先ほどの誤解の1つが解けてくるかなと思います。

最初の説明の中に「エンジニアはドメインエキスパートの通訳者だ」っていう誤解があったと思うんですけど、これはどういうことかというと、上記スライドの左の図なんですけど、ドメインエキスパートからエンジニアが、そのドメイン領域の知識を得て、それを他のステークホルダーに伝えたりコードに落とし込む。で、コードからソフトウェアを生成して、それをもってドメインエキスパートに還元して、いいね悪いねっていう反応を受けてさらに改善していくっていうのが結構多くの現場で見られるんですけど、こうではないということです。DDDにおいては、ドメインモデルによってエンジニアとドメインエキスパートおよびステークホルダーは相互にコミュニケーションを行うと書いてあります。つまりここでは右の図なんですけど、共有メンタルモデル(ドメインモデル)っていうのを介して、関わるすべてのステークホルダーが共通認識を持つというのが正しい理解です。

DDDを成すモデル駆動設計の構成要素

次に、DDDをなすモデル駆動設計の構成要素を説明します。説明といってもここではちょっとお見せするだけなんですが、DDDを構成する要素には次のものがあります。

まず1つがドメイン層の分離で、これは書籍ではレイヤードアーキテクチャと紹介されてるんですけど、ヘキサゴナルアーキテクチャなどのいわゆるレイヤー構造を持つアーキテクチャによって分離してね、というものです。さらにソフトウェアによるドメインモデルの表現手法として、エンティティ、値オブジェクト、サービス、モジュール(パッケージとも呼ばれます)などがあります。で、ドメインモデルの中でエンティティと値オブジェクトのライフサイクルをつかさどるものとして、集約・ファクトリ・リポジトリというパターンがあります。

「レイヤー構造を持ち込めばDDDになる」という誤解に対する回答

この説明でまた1つ誤解が解けたことになります。どういう誤解かというと、「レイヤー構造を持ち込めばDDDになる」っていう誤解です。

多くの記事で「○○アーキテクチャを用いたらDDDになった」というような記述があるかと思うんですけど、そうではなくて、DDDの書籍にあたると、そもそも「ユーザインターフェースやデータベースを操作するコードを、ビジネスロジックとして扱うためのドメイン層から分離することで、初めてモデルの設計開発が可能になる」と書いてあります。

つまりどういうことかというと、レイヤー構造などを用いて関心とか責務を分離するっていうのは、そもそもDDDをやる上での土台になることだから、レイヤー構造を持ち込んだからDDDになるわけじゃないよっていうことが主張されています。

DDD関連の用語の整理

あとはDDDに関する用語の整理ですね。

エンティティというのは一意性を示すためのオブジェクトです。値オブジェクトは、システム固有の値を表現するためのオブジェクトまたは型であることが明示されています。この他にも、サービス・モジュール・集約・ファクトリ・リポジトリについても用語の説明があります。ここら辺に関してはかなり厳密に定義されているので、興味のある方はDDD関連の書籍や、今回の発表資料を読んでみてください。

さらに二つの誤解を解く

用語がこのように定義されていることがわかると、次の2つの誤解が解けるかなと思っています。

まず1つは「DDDの構成要素をすべて利用しなければいけない」っていう誤解なんですけど、これも結構あるあるですね。例えば、レイヤードアーキテクチャを用いて、ドメイン層にエンティティを作ったり値オブジェクトを作ったり、それを集約して、さらにそれをファクトリにして、みたいな。で、それ以外のものはサービスやモジュールにして、みたいな。こんな感じですべて入れないとDDDじゃないよみたいな誤解があると思うんですけど、そうではなくて、サービスとかモジュールっていうのは責務や概念を分離するものなので、必要なときだけ使います。あと、ファクトリも集約の生成が複雑になった場合の手法なので、必要じゃなければ使わなくていいということです。

また、これもあるあるですが「データの詰替作業がとてもツラい」と。これは確かにそういう側面はあるんですが、そもそも、データの詰め替えがなぜ発生するかというと、それは責務や概念を分離してレイヤーに閉じ込めているからだという前提があります。なので、そもそも不要なレイヤーを設けていないかとか、そもそもドメインモデルの設計が正しいかどうかを、そのとき確認した方がよいと考えています。

オブジェクトストレージにおけるDDDのプロセス

ここまでで、DDDの整理、そして言葉の整理ってのができたので、我々オブジェクトストレージチームではどのようにDDDを進めているのかというお話をしたいと思います。オブジェクトストレージチームでは「Domain Modeling Made Functional」っていう書籍を参考に、このプロセスを実践しています。

まず最初にやることがドメインの理解です。ドメインの理解においては、ビジネスイベントを介したドメインの理解ということで、ビジネスはデータの変化の連続であるととらえてドメインを理解しようとします。ドメインを理解できたら、次にやることはユビキタス言語の定義です。これはドメインエキスパートとステークホルダーの共通言語を持つという行為になります。3番目は、ドメインを自然言語で定義します。4番目はドメインモデルの深化で、これはドメインモデルをより練度の高いものにするということになります。

登場人物の整理

これからストーリー仕立てでDDDを語っていきたいと思うので、登場人物を整理したいと思います。

当社におけるオブジェクトストレージのサービスは幅広いステークホルダーが存在していて、課金とかサービス全体の設計を行う企画の人とか、あとはオブジェクトストレージを良く知っているドメインエキスパート、そしてバックエンドAPI等を開発する開発チーム、そしてハードウェア・ネットワーク・ファシリティ層を担当するインフラチームという4つの登場人物がいます。

ビジネスイベントを介したドメインの理解

ではまず初めに、ビジネスイベントを介したドメインモデルの理解から説明していきたいと思います。ここではオブジェクトストレージの機能をベースに説明したいと思うんですけど、オブジェクトストレージの機能は上記のスライドに示す通り6つあります。

オブジェクトストレージチームでは明確に誰がドメインエキスパートであるかは定まってはいませんが、ここでは説明のためにドメインエキスパートと表記します。で、私がアサインされた際にDDDというのは意識されていなかったんですが、それに近しいものとして以下のようなやりとりがありました。

まずエンジニアAが「ユーザが弊社のオブジェクトストレージを利用しバケットを作成したいというビジネスイベントが発生したとして、弊社のオブジェクトストレージを利用するためのトリガーは何ですか」と尋ねます。これに対してドメインエキスパートは「まず前提として、クラウドコンパネにログインしてサイトを選択する必要があります」と回答します。

続いてエンジニアBが「サイトとは何ですか」と聞いています。これに対してドメインエキスパートからは「サイトというのはバケットを格納する物理位置のことで、さくらのクラウドで言えばゾーンだったり、Amazon S3で言えばリージョンに相当するでしょう」という回答がもらえました。

ここまでの話で、エンジニアAとBには、クラウドコンパネログイン→サイト選択というフローが見えてきました。

さらに続きます。エンジニアAが「サイトを選択した後、すぐにバケットを作成できるんですか」と聞きます。ドメインエキスパートは「サイトに紐づくアカウント、これをサイトアカウントと言うんですが、これを作成しておく必要があります。サイトアカウントはクラウド側の契約と紐付けるために存在しています」と回答します。エンジニアBは、サイトアカウントがバケットを所有することを理解します。なのでここでは、サイト選択やサイトアカウントの作成がフローとしてあるんだなということがわかりました。

続いて、エンジニアAが「サイトアカウント作成後にバケットを作成できるんですか」と聞きます。ドメインエキスパートは「はい」と答えます。ただ「オブジェクトストレージコンパネではサイトアカウント作成に際して同時にアクセスキーを発行しています」と付け加えます。これに対してエンジニアBが「アクセスキーの作成は必須ですか」と聞きますが、ドメインエキスパートは「コンパネではそのようにしてるだけで必須ではないですよ」と答えますので、ここはフローはつながっていないと理解できます。

次にエンジニアAが「ところで、バケットにはどのような制約がありますか」と、バケット作成における制限事項を確認します。ドメインエキスパートは「バケット名はオブジェクトストレージ全体で一意である必要があります」と答えます。エンジニアBは「なるほど。サイトを跨いで同じ名前のバケットは作成できないっていうことですね」と理解しました。「はい、そうなります」とドメインエキスパートは答えます。さらにドメインエキスパートからは「それ以外にもオブジェクトストレージ全体で制約を加えたいものがいくつかありますね」という回答があったので、バケット名以外にも何らかの制約を全体で加えたいという要件があるということがわかりました。なので、バケット作成の前に何らかの制約を加えるために、オブジェクトストレージ全体で一意性を管理する何らかのレイヤーが必要だということがわかりました。

そして、これに加えてドメインエキスパートはこのように続けます。「バケットごとに細やかな権限管理ができるパーミッションという機能もあります。例えば、特定のバケットを操作するためのパーミッションキーを発行して、それを特定のバケットや制限された操作を担当するチームに割り当てる運用が可能です」と。つまり、細やかな権限管理のためのパーミッション作成やパーミッションキーの発行という機能も有してることを主張していますので、コンポーネントを追加します。

ここまでくると、ドメインとして境界づけられたコンテキストが定義可能かなと思います。そもそも境界づけられたコンテキストというのは、ドメインモデルを適用できる範囲を限定したものということなんですけれども、これを適用すると、オブジェクトストレージシステム全体で何かやらなければいけないものをFederationコンテキストと名付けて、サイトごとに何かやらなければいけないものをSiteコンテキストというふうに名付けることができます。

ユビキタス言語の定義

では次にユビキタス言語の定義を行います。私がアサインされた後に、ユビキタス言語またはそれに相当するものに関連する次のやりとりを行ったことがありました。

まず企画の人が「クラスタの追加は20XX年を予定しています」と発言しました。これに対してエンジニアBは「クラスタとはk8sのことを指してますか? それともコンパネに表示される石狩第1ゾーンのような物理的位置を指していますか?」と。ここで何が起きてるかというと、クラスタという言葉が何を指してるかわからず混乱したという話ですね。ここでエンジニアCが「オブジェクトストレージの物理的位置を指す言葉は"サイト"という別の名称にするのはどうでしょうか」というふうにして言葉を整理しました。これは共通認識ということにしましょうということでチーム内の合意を得ることができました。

このようにしてユビキタス言語をチーム内で定義して、どの用語がどのような意味を指すのかっていうのを明確にすることができました。ちなみにこれはオブジェクトストレージチームのドキュメントとして表にまとめています。

ドメインを自然言語で定義

ユビキタス言語を定義した後はドメインを自然言語で定義する必要があります。

ここで私が学んだことというか、いくつかの書籍や私の経験からわかったことなんですが、ドメイン、境界づけられたコンテキスト、コンテキストマップ、ユビキタス言語が定まると、私達エンジニアはすぐにコードに落とし込みたくなってしまうんですよね。ただ、一度の議論でドメインやコンテキストを正確にとらえることは難しいです。それらを整理するツールとして、例えばUMLとか、それに準ずるツールであるPlantUMLというのもあるんですが、これの正しい意味とか使い方の学習が必要で、それがまた難しいという問題があります。そういうときに便利だなと思ったのが、自然言語または擬似的なプログラミング風のミニ言語でドメインを定義するっていう方法です。

どういうことかというと、例えば、境界づけられたコンテキストにおけるFederationの場合、トリガーがオブジェクトストレージコンパネからのバケット作成要求で、入力がバケット名とクラウドアカウントに関するパラメータ群、そして出力がバケット名とサイトIDっていうふうに表記できたり、サイトも同じように表記できます。

バケット作成におけるデータ構造

では、ワークフローはいいとして、データ構造はどうなるか考えてみましょう。例えば、Federationコンテキストにおけるバケットのデータ構造ですね。バケット名(Name)とサイトID(SiteID)、そしてStateはわかるけど、副作用として何があるのかはまだわかっていません。

同様に、Siteコンテキストにおける副作用とバケットのデータ構造もフィールドもわかりきっていません。

なので、こういう場合はドメインエキスパートと協力しながら明らかにしていく必要があります。会話を続けることによって、わからなかったフィールドや副作用がわかるようになります。ここで示されたことは何かというと、サイトで作成されたバケットは、バケット料金を計算できるように課金システム連携が必要で、そのためにリソースIDを登録する作業が発生することがわかりました。

ドメインモデルの深化

プロセスの最後はドメインモデルの深化です。これはビジネスの変化に合わせてドメインモデルを変えていきましょうねっていうことを主張しています。

まとめ

最後にまとめです。

そもそもDDDとは、ドメインモデルをとらえた上で、それをコードに落とし込む設計思想です。また、DDDや関連する書籍で紹介されているテクニック、例えば値オブジェクトやエンティティ、サービスを用いるだけでは、ドメインを正確にとらえることはできません。ドメインのコンテキストやコンテキストマッピングをとらえる戦略的設計と、ドメイン内部をモデリングする戦術的設計の両方を用いる必要があります。そしてDDDは、そのプロセスの中でドメインエキスパートおよびステークホルダーが協調してドメインモデルを深化させ、ビジネスの変化に対応する必要があります。

以上で発表を終わります。皆さんの現場でDDDを進める参考になれば幸いです。

発表資料