DDD的なことを今後進めていく上で、自分として課題としている論点をまとめてみた。あくまで私の現時点での理解度を前提に、そこでの個人的課題感を取り纏めてみたものなので、不足や誤りや過剰が多々あるだろうがご容赦を。そして、アドベントカレンダーの初日、続く議論の礎となり、3人の賢者24人の荒ぶる者によってベツレヘムの星が見出されることをただ願うのである。
◇
あらまし
-
DDDのいわゆる戦略の言う“広義のドメイン”と、戦術パターンにおける“狭義のドメイン”。この二つの用語は、実のところ「異なる境界付けられたコンテキストに属する語彙」として扱った方がよいのではないか?
-
参照系と更新系は、その本質として非対称性があって、参照系向けの新たな戦術パターンが必要なのではないか?
-
「コンテキスト」は、強く分離する上位階層から緩く分離する下位層まで、多段階の階層構造に在ると捉えるべきではないか?
-
(「3.」を前提に、)「コンテキスト」と「(狭義の)ドメイン」は多対多の関係にあり、ドメインはコンテキストが定めるスコープの配下に整理できる、というものでは無いのではないか?
-
Evans氏がDDD本を上梓した頃と現代では、それこそ前提となるコンテキストが相当に異なってきていることは常々念頭に置いた方がよいのではないか?
◇
以下それぞれについて述べる。
1. 「広義のドメイン」と「狭義のドメイン」は使い分けないとならないのでは?
まず、DDDのいわゆる“戦略”を重視しているか、目下の“戦術パターン”に取り組んでいるか、そういった立ち位置の違いから、「ドメイン」という用語へ込める意味合いに差異が出ている場面がある様に思う。
- 広義のドメイン
- ほぼドメイン工学のいう「ドメイン」であり、何らかの問題領域に対するスコープの認識を表している。よって、、「何だって対象になる。どんな問題領域にも、その問題領域としてのモデルは描ける。ただし、当該ドメインのモデル化技法自体、そのドメインに深くダイブすることでのみ初めて見出される。」、、といった主張となる。文脈を規定するにも、語彙を収集するにも、それ以前に、ドメインの“先住民”の風習の観察から始まる、こととなる。(※「ドメインの“先住民”」は「ドメインエキスパート」を再定義する用語としてとても良い気がしてきた。)
- 「広義のドメイン」を用いる立場からみると、「狭義のドメイン」を用いている人たちが、プレゼン層やインフラ層をモデルの外部として、それこそドメイン外=定義域外として、実質的に取扱対象外としているような様子が片手落ちに見える。確かにアプリ層やドメイン層には、例えばEntityといったモデル化技法が有用だとしても、プレゼン層(≒UI)には(自分はよく知らない領域だが、)それとしてのモデル技法があるはずだし、インフラ層には、(これはだいたい知っているが、)RDBのERモデルがよく使われている。WebベースUIにはWebベースUIの、RDBにはRDBの、もちろんOO言語によるロジック記述にはそれとしての、それぞれの“ドメイン”にそれぞれのモデル化技法があり、それら全てを高次の階層で統合してこそのシステム・アーキテクチャー、という話になる。
- 狭義のドメイン
- 粗い理解としては、、MVC改めの「プレゼン層-ビジネスロジック層-データ層」なる論理3層アーキテクチャーの改善系として、論理4層を提唱しているところのものである。プレゼン層とデータ層(=DDD的にはRepository以下インフラ層)はいいとして、ビジネスロジック層が単層ではうまくない場面があって、上層のアプリ層と下層のドメイン層に分けたらより良かった、、という発展のストーリーとして位置付けることができる。このとき、個々のユースケース(個別の要求)によって揺らぐことのより少ない下層側を、アプリケーションから基本的には独立したドメイン、と捉える。
この二つの「ドメイン」は、正直、“異なる境界づけられたコンテキストに属した、異なるユビキタス言語の語彙”になっていると思う。だから、同じ「DDD」について語っているようでも、人によって「ドメイン」という語で何を言い表そうとしているか、実際よくずれている。
ということは、本記事における「ドメイン」も、どちらのコンテキストに於けるものか明示する必要がある。本記事の以降で言及する「ドメイン」は、(明示的に修飾されてない限り、)全て「狭義のドメイン」である。
2. 参照系と更新系は、その本質として非対称なのではないか?(参照系向けの新たな戦術パターンが必要なのではないか?)
CQRSによって、特に高トラフィックの場合に、参照系(Read系)を別サブシステムに分離するという方式が一般に定着した。このとき、概念的に同一のデータ種(※例えば、「ECにおける商品カタログ」、といった意味で同一)であっても、参照系(Read系)と更新系(Write系)とで、異なるService/Entity/Repository設計となるであろう。というか異なる設計や実装とできるように分離しているわけだ。
イベント伝播で連携させる本格的なCQRSを導入するまでもなくとも、単一RDBベースであっても、厳格にService/Entity/Repositoryを導入するのが、参照系ロジックでは(※例えば、「商品一覧」といったユースケースで)過剰に感じたり、逆にSQL JOINするのに集約跨ぎについての設計上の折り合いをいちいち付けなければならないことが不自由に感じたり、してないだろうか。
たぶん、ここには何か本質的なテーマがあると思う。
(CQRSといった大掛かりなアーキテクチャーを必要としない場面でも、)そもそも本質的に、更新系ロジックは厳格でいたいのだが、参照系ロジックはもっと自由でいたいのだ。たぶん、Service/Entity/Repositoryといった厳格なパターンは、更新系には相応しいかもしれないが、参照系には何かもっと別のやり方が必要、という要請が現に存在するのだ。
SQL JOINするのはDDD的にどうなのであろうか?では、GraphQLはどうなのか?「JOINを含む複雑なSQL SELECT」とGraphQLとは、意味論的に何か差異があるのだろうか?(分散データソースを跨いでの"JOIN"なり"Mash-up"なりができるという付加価値はあるにしても、)もし意味論的にほとんど同じなのだとしたら、違いは何か?違いはその処理が置かれるレイヤーである。参照系ロジックにおける"Mash-up層"が、サーバサイドあるいはバックエンドの奥深くにあるのが嫌なのだ。やっぱりBFF(Backend-for-Frontend)くらいに置きたいのだ。なぜか?参照系ロジックに対する要件は、基本的にデータ利用側(=フロントサイド側)から「これこれこのようにデータを見たい」という形で発せられ、対して、更新系ロジックに対する要件は、基本的にデータ提供側(=永続化データオーナー側)から「このようにのみ更新可能とさせたい」という形で発せられる。要件のオーナーシップが、参照系ではフロントサイドにあり、更新系ではサーバーサイドにあるのだ。参照系ロジックは相対的にフロント側に責務があり、更新系ロジックは相対的にサーバーサイドに責務がある。、、たぶんこういうことになっているのではないかと考えている。
3. 「コンテキスト」は階層的に在ると捉えるべきではないか?
もはやローカル定義に無しに「マイクロサービス」と云うのはただ混乱の元ではあるが、まあ構わず進めてみる。何かアプリケーションをマイクロサービスに分割した時、それぞれのマイクロサービスは異なる「(境界付けられた)コンテキスト」を構成するのであろうか?まー、だいたい「Yes」というのが相場かと思う。
ただ、ちょっと「境界付けられたコンテキスト」の戦略的な意味を思い出すべきである。「境界付けられたコンテキスト」とは、ある一式のユビキタス言語語彙セットが通用できるスコープのことのはず。そこにある二つのマイクロサービスは、ユビキタス言語セットとしても確かに分断されているという程に分断されているのだろうか?
コンウェイの法則または逆コンウェイの法則として、マイクロサービス分割とチーム構成は相似形であるべき、とされる。そうだとして、複数のマイクロサービスそれぞれを担当する複数のチームは、ユビキタス語彙セットを共有していない/できない、というほどに独立しているのだろうか?まー、程度の問題なのだとは思う。
そう、程度の問題なのだと思う。だとしたら、コンテキスト分割の程度も程度の問題、として扱うべきではないか。
EC事業でAmazonなどのモールや決済などで外部サービスと連携する場合や、大企業グループ内システムで他部門の明らかに独立に運営されているシステムとデータ連携するような場合は、自分たちのシステムと彼らのとの間には、明確に「境界付けられたコンテキスト」の境界が引かれている/引くべきである、という考えで妥当であろう。しかし、隣のチームのマイクロサービスとの間には、そこまで明確な境界はないであろう。仮に共有カーネルするとしたら、例えば実は50%くらい共有することになってないか。それって、(境界付けられた)コンテキストとして異なっている、と云う必要がある/云うべき/云うのが妥当、なのだろうか?
結局のところ、“自分たち”のシステムを最大粒度で捉えたときに最低一つの明確な境界付けられたコンテキストは必ず存在するが、その内部構造としては、自分たちのチーム編成に対応して開発・運用されるようなマイクロサービス構成などについては、よりゆるく分割されている“サブ(部分)・コンテキスト”といった概念で捉えるのが妥当ではないだろうか。
このようなコンテキストの階層的理解を導入すれば、CQRSなどでの参照系と更新系の分離も比較的位置付けやすくなるであろう。即ち、「概念的に同一のデータ種であり、大粒度の境界付けられたコンテキストとしては同一であり、共通の語彙セットの元にある。ただ、参照系と更新系で小粒度のサブ・コンテキストは異なり、コードの共有はされない(もしくは共有の程度が少ない)、としている」と。
加えて、一度階層の存在を容認するならば、それは2階層であると規定する理由は無く、理屈の上では多段階に展開し得るとする。
4. 「コンテキスト」と「ドメイン」のインピーダンス・ミスマッチ、"Beyond DDD"としてのDCIもしくはサブジェクト
結論からいうと、個人的には『「ドメイン」は一つの「コンテキスト」に収まっている』という考えに疑問を持っている。つまり、『「コンテキスト」を跨いで横断的に存在し得るような「ドメイン」』、というものを積極的に考慮しなければならないのではないか、と考えている。
「共有カーネル」という考えは、既に「境界付けられたコンテキスト」の概念とコンフリクトしていると思うのである。「境界付けられたコンテキスト」が異なれば定義上、語彙セットのシェアは出来ないはずである。が、語彙のサブセットをシェアした方が“現実的”なとき、共有してしまえ、という。「共有カーネル」というパターン自体は現実に当然に必要なアプローチではあるが、「共有カーネル」が必要と思われた時点で、当初想定のコンテキストを横断してドメインが存在していることを示唆しているのではないか。
もちろん、単なる言い方、捉え方の問題である。「分断されたコンテキストが原則で、例外的にシェアもある」と捉えるのか、「一般にドメインはコンテキスト横断的である。ただ、コンテキストを跨がないように統制することができるならその方がシンプル。」と捉えるのか。
重要な補足となるが、本節での「ドメインはコンテキストを跨ぐ」という考えは、前節での「コンテキストは階層的に在ると捉える」という考えの導入を前提とする。コンテキストが階層的で無いとするならば、比較的大粒度の唯一の境界付けられたコンテキストだけが存在するだろうから、確かに自ずとドメインはコンテキストに収まる。しかし、マイクロサービスという現実などを見ると、たぶん“サブ・コンテキスト”といった緩い分割や、さらにその階層が多段階に展開されるだろうことを扱った方がよいと思える。だとしたら、ドメインはそのようなサブ・コンテキストやサブ・サブ・コンテキストを跨いで横断的に存在することになるだろう、という主張である。
・
この考えにはネタ元がある。次の萩原正義氏による2005年の連載である。
この連載の中のこちらのページの中(※ページ中程の「(5)要求モデルとソフトウェア資産モデルのインピーダンス・ミスマッチ」の節)に言及があるところの「何をしたいかを表す要求モデル」と「どうあるべきかを表す資産モデル」である。端的に、DDDのコンテキストは要求分析あるいはロードマップから得られたスコープであり、DDDのドメインは、まさに対象システムがどうあるべきかの構造であろう。上記萩原氏の記事における「要求モデル」と「資産モデル」が、DDDのコンテキストとドメインに対応すると見ることが妥当で、かつ萩原氏の「インピーダンス・ミスマッチ(=上位層から下位層への接続がツリー構造とならない)」認識が妥当だとしたら、DDDのコンテキストとドメインにもインピーダンス・ミスマッチは存在するはずだろう、となる。
・
もう一つのネタ元は、Coplien氏らのDCIである。
https://en.wikipedia.org/wiki/Data,_context_and_interaction
https://sites.google.com/a/gertrudandcope.com/www/thedciarchitecture
https://klevas.mif.vu.lt/~donatas/Vadovavimas/Temos/DCI/2009%20The%20DCI%20Architecture%20-%20A%20New%20Vision%20of%20OOP.pdf
Coplien氏は、おそらく世界で一番(※もしかしたらEvans氏自身よりも)DDDのエッセンスを理解していて、そして、コンテキストとドメインのインピーダンス・ミスマッチ問題(および、DDDの戦略と戦術パターンの話の間の論理ギャップ)に対して、おそらく世界で最も強烈にマサカリを投げている。いくつかリンクを貼っておく。
https://dddeurope.com/2016/jim-coplien.html
https://togetter.com/li/877728
https://togetter.com/li/927916
https://togetter.com/li/1062864
DCIとDDDの共通点は、、DCIの「コンテキスト」は、DDDのコンテキストにほぼ対応する。ただし、「境界付けられたコンテキスト」と云うほどの大粒度さはなく、本記事中での“サブ・コンテキスト”程度の粒度感。また、DCIの「データ」は、DDDのドメインにほぼ対応する。そして違いは、、DDDでは、「コンテキスト内にドメインが収まる」とするが、DCIでは、コンテキストとデータは直結せず「ロール」を介する。この「ロール」がインピーダンス・ミスマッチ問題への解となっている。
・
私は、全てのDDD実践者に、同時にDCIに取り組むことを強く薦めたい。(あるいは、荻原氏の記事でのサブジェクト指向でも良い。)
DDDは、従前のMVC改めの「プレゼン層-ビジネスロジック層-データ層」なる論理3層アーキテクチャーを改善し、ビジネスロジック層を単層から、アプリ層とドメイン層の二つに分離した。この垂直分離については、荻原氏の記事では「ユースケース-ドメインオブジェクト」として、DCIでは「インタラクション-データ」として扱っており、これら三つはほぼ対応している。水平分割についてはどうか?荻原氏の記事では、ユースケースとドメインにはインピーダンス・ミスマッチ(=水平分割におけるトレーサビリティ・ギャップ)があるとして、そのズレを仲裁する「サブジェクト」を導入している。DCIでは、同様にインタラクションとデータを仲裁するものとして「ロール」を導入している。しかし、DDDは、水平分割について何も言って無い。
5. "DDD isn't done"?
Evans氏のDDD本が出版されたのは2003年。日本で最初に一般に広まったのはたぶん'05年〜'06年からくらいで、当時オンライン記事としてはオージス総研社の解説記事くらいが頼りだった。
当時と今とでは前提環境が相当に異なる。当時DDDへの関心を持っていた者は、エンタープライズ系のシーンにいる者たちであり、ほとんど単一RDB前提で、スケーラビリティへの考慮はそれほどは不要で、コンテキストが交錯する状況も少なかった。現代のDDD実践者は、DDDの戦術パターンはそういう時代に形作られたという歴史的背景は念頭に置いたらよいと思う。もちろん、プラクティスの基礎としては依然有用である。しかし、CQRSやMSAの考えをDDDにリンクさせての実践手法が試みられている現代、DDD本の戦術パターンを教条的にやってるだけでは上手くいかないような場面が多くなった。一方、DCIやサブジェクト指向といった、DDDの戦術に大きく欠けていると云える部品を補った論は既に存在している。(※DCIは2009年発表、前出の萩原氏の記事は2005年のものである。)
Evans氏自身、"DDD isn't done"(≒未完了である)としてある種不完全さを認める発言をしているようである。(※単に、進化は止まってないぞ、というニュアンスかも知れないが、いずれにせよ防御的な発言であろう。)
https://www.infoq.com/news/2018/09/ddd-not-done (原文)
https://www.infoq.com/jp/news/2018/10/ddd-not-done (日本語訳)
(DCIやサブジェクト指向が何を言おうが、)実のところ、DDDの最大価値は、本記事冒頭で示した「広義のドメイン」に基づくところの戦略論にある。DDDの戦術パターンは、一つのメタモデルを目指したと言えるが、DDDの戦略論に基づくなら、特定の具体(メタ)モデルを提示した段階で、既に特定の(広義の)ドメインを念頭に置いている、こととなってしまう。DDDの示す戦術パターンは、ある種の業務システム構築上のプラクティスとしては有用だが(※本記事もほとんど戦術パターンの話だし)、最大価値である「対象領域を対象領域自身の言葉で表現するのだ」という行動原理の価値を薄めてしまったかもしれない。このへんは、上記InfoQの記事によると、Evans氏自身考えあぐねているようである。