背景
分散アーキテクチャにてデータをデータの側面での境界付けられたコンテキストである
データドメインごとに分割した後には、どのサービスがそのデータドメインを所有するか?
を考えないといけない。
これは分散アジャイル組織において、情報をどこに持たせるのがもっともベターか?
を考える際にも超重要になってくるので、組織設計を行う方とかは是非とも一度通っておくことをオススメします。
いきなり分散アジャイル組織をデザインするのではなく、
まずはどのチームにどのデータを所有させるとよりQCDを満たせる解として良いのか?を
考えられるので。
前提知識
データドメイン
端的に言うと、データの側面でのコンテキスト境界のこと。
トランザクション単位といったプロセスの側面から考えたうえで、
強い整合性の求められるような箇所(注文と注文明細とか)は、同じデータドメインにまとめるといったことを考えます。
このデータドメインは、データという側面からの考察だけでは十分に明らかになりません。
必ずトランザクションなどといったプロセスの側面とを行き来し、
データのコンテキスト境界を明らかにします。
概念スキーマで論理分割
データベースに詳しい方ならここはすっ飛ばしていただいて構いません。
今回扱うソリューションは、ソフトウェアアーキテクチャハードパーツにある代表的な解について、自分なりの見解を交えながら記載しますが、
いきなりサービスとして分割する前に、まずは物理的に1つのDBサーバーを基本として、
その中でデータのコンテキスト境界ごとに論理分割した上で、
各モジュールに所有権を割り振ったのちに、
「この境界の位置で物理的に切り分けて問題ない。切り分けた方がビジネス上の恩恵がデメリットより上回る」と判断した上で1つのサービス境界内に、サービスとデータをカプセル化してください。
情報エキスパート
この時、考え方の基本になるのは、GRASPの情報エキスパートパターンである。
テーブルに対して書き込みを行うサービスにその所有権を担わせる。
アナリシスパターン
また以下で出てくる共同所有におけるテーブル分割パターンでは、
アナリシスパターンで出てくる【知識レベルと操作レベルの分離】の思想が反映されている。
マスターデータである知識レベルが1つに対して、実体として手に取って扱える操作レベルの方が複数あるようなビジネス。
たとえば某ファストファッションビジネスなどで、各物理的な商品の方が、
どの顧客の手にわかっているのか?までシステムで管理したいスコープとする場合には、
操作レベルの方の商品を【在庫】と名前を表現して、
【商品】はマスターデータの方の知識レベルの方として表現することで、
マスターデータである商品の概念1つに対して、在庫は0以上であり、
在庫と顧客が注文などを通して関連づき、商品と顧客は紐づかないという概念モデルとして表現する。(以下の図を参照)
ただし、マスターデータである知識レベルが1つに対して、実体として手に取って扱える操作レベルの方が必ず0または1になるようなビジネスモデル。
たとえばオーダーメイドのようなビジネスなどでは、わざわざ知識と操作レベルの概念を分けるメリットはない。
諸注意
サービスを分割してからデータの所有権を考察するなんてことは絶対にしないでいただきたいです。
分散アーキテクチャにおける他のコンテキストへのデータアクセスやトランザクションの問題は相当やっかいです。
あらかじめ「もしも仮にサービスを物理的にも分割したら~」という仮定で、
嬉しいことと、それによって発生するデータの整合性リスクや、
所有権を適切なコンテキストに割り当てたとしても、その後に状況が変わって、
データの割当先を別のサービスコンテキストに変えた方がベターになる可能性だってあります。
それらの分割することによって発生する脅威を十分に考えたうえで、
それでもマイクロサービスとして切り分けた方が、ビジネス上恩恵があると判定された場合にのみ、最もリスクが低くて可逆性を担保できる場所から切り分けるというように考えます。
上記で書いたように、まずはモジュラーモノリス構造にて、
概念スキーマによってデータ群を論理分割し、各スキーマ内のデータドメインを
各モジュールに所有権を割り当てというようにすることを目指しましょう。
どのモジュールが概念スキーマによって分割されたどのデータドメインを所有すると思われるのか?は、チーム内で共通認識が持てるように、しておきましょう。
所有の仕方
所有の仕方には大きく分けて3種類存在する。
根底は情報エキスパートの考えだが、それだけでは全体所有や共同所有には対処しきれない。
まずは3つのそれぞれの概要について。
単独所有
下図のように
サービスAはテーブルXに対してのみ書き込みを行い、
サービスBはテーブルYに対してのみ書き込みを行い、
サービスCはテーブルZに対してのみ書き込みを行うという場合。
つまり1つのテーブルに対して、1つのサービスしか書き込みをしないという場合。
この場合には非常にシンプルである。
以下のように境界を定義し、カプセル化する。
まずはこの単独所有ができないか?を考える。
それができなかった時に、次の全体か共同所有を考えるようにする。
全体所有
1つのテーブルに対して、ほとんど(またはすべて)のサービスから書き込みが起きる際には、
全体共有が生じる。
問題点
数十、数百以上のサービスが1つのテーブルに対して書き込みを行う場合、
せっかくデータドメインごとに分割したのにもかかわらず、
データドメインごとに分割する前の問題点であった
・変更の制御
・データベースへのコネクション枯渇
・スケーラビリティ
・耐障害性
のすべての問題が再発してしまう。
ソリューション
たとえば以下のような図において、テーブルXは既存のすべてのサービスから書き込みをされているとする。
この程度のサービス数であれば問題ないかもしれないが、
実際問題これがより大量の数十などのサービスから書き込みをされていたとしたら、
今のままでは上記のような運用特性上の問題が起きてしまう。
そこで以下のように、テーブルX専用の所有者であるサービスを設けて、
他のサービスAやBなどがテーブルXに書き込みを行いたい時には、
そのサービスに情報を送信して、Xの所有者(図のX所有サービス)自体が書き込みを行う。
図には描いていないが、永続的なキューを使用してメッセージを送る。
そうすればメッセージ送る先とかに障害が起きたとしても送りたい情報は失われないことを保証できる。
共同所有
上記のすべてやほとんどのサービスがテーブルを所有するのではなく、
一部の複数のサービスが同じテーブルに対して書き込みを行う場合にこの共同所有がある。
この共同所有には、いくつかのパターンにわかれる。
ただし、その中でデータドメインパターンについては、
マイクロサービスとは言えないアーキテクチャスタイルに感じるため、
個人的にはあまりオススメできない。
テーブル分割パターン
このパターンはアナリシスパターンの知識レベルと操作レベルの分割に近しい考え方である。
知識と操作の分離とは、たとえば対象のビジネスにおいて、
商品のマスターデータに関するものと、手に取れる物理的な商品とを分けて考えたいという場合に、概念モデル上でも分離して考えることである。
マイクロサービスでいうと以下の図のように
知識レベルの方の商品のコンテキスト境界である【カタログサービス】
操作レベルである方の商品コンテキスト境界である【在庫サービス】
もともと1つであった商品テーブルをこれら2つに分割することで対応する。
ただし、この場合には知識レベルの商品側と操作レベルの方の商品とのデータの整合性などの問題点がどうしても避けられない。
メリット
①データの単独所有
なんといっても単独所有にできるので、非常にシンプルになることだ。
ただし、知識レベルと操作レベルの方にデータの強い整合性が求められるようなケースの場合には、これはこれで問題となる。
②
デメリット
データドメインパターン
このパターンはマイクロサービスとはまだ言える状態ではない。
マイクロサービスにおける1つの境界付けられたコンテキスト内には、
オブジェクト指向のカプセル化と一緒の考え方で、
テーブルとそれに対する書き込みをするサービスプロセスをまとめ、
他コンテキストからのテーブルへの直接アクセスを禁止(データ隠蔽)すること。
事実
これに対して、データドメインパターンはマイクロサービスの量子としては、
まだ未完成状態である。
テーブルしかないデータドメインのみを1つのコンテキスト境界内に定義しているからだ。
主張
よって、まだどのサービスにテーブルの所有権を持たせるのかがまだ不明といった場合に、
一時的にこのパターンを適用する際には構わないと感じている。
チームトポロジー書籍の物理本、P.89に「チームをまたぐ重要な情報を視える化することは~」という一文がまさにこのデータドメインパターンの組織構造を指していると思われる。
余談ではあるが、自分は案件の中でこのパターンのアーキテクチャを見たことがある。
それは超大規模エンタープライズであり、
あるビジネスプロセス群を管理する組織と、別のビジネスプロセス群を管理する組織が分かれていた。そしてデータ基盤をマネジメントする組織はまた別に存在していた。
ただし個人的には、サービスとして分割する前に、
上図でいうとカタログモジュールと、在庫モジュールに論理分割しておいて、
それらを含む1段階マクロなコンポーネントとして、商品サービスを定義し、
その内部に商品テーブルを持つというようにする方がベターと感じる。
この場合、在庫とカタログに関する計2つの責務が商品サービスには存在することになるが、そのデメリットを覆うだけの利点の方が上回る。
メリット
デメリット
下図をご覧いただければお分かりかと思いますが、データスキーマが変更されたら
それに伴ってそれを使用するサービス側への変更時の問題である。
①データスキーマの変更によるサービス数の増加
境界を飛び越えての変更の波紋が伴うことから、
そのテーブルを使用するサービスをすべてマネジメントできていないと、
どれかのサービスが落ちるなどの問題が起こる。
なので、対象のデータドメインを使うサービス数が数個であればまだいいが、
数十とかってなってくるとどれか考慮漏れが起きたりしかねない。
②データスキーマの変更によるテスト範囲の増加
データスキーマが変更されたら、当然それを使用するサービスプロセス側もすべてテストし直す必要がある。
サービスが数個ならまだかわいい方だが、これが数十とかになってくると、
テストコストは絶望的に高くなることは定性的にも容易に想像がつく。
③ データ書き込み権限の統制を取るのがめんどう
サービスを開発する側からすると、
密なコミュニケーションがないと、「あ!ちょうどあのデータ使いたかったんだよラッキー」みたいなことが起こりかねないので、書き込みを行える制限をかけないととんでもないことになるのは自明である。
④データスキーマの変更によるデプロイコストの増加
データドメインの量子と、それを使うサービス量子のすべてで実質1つのデプロイ量子
であることから、その範囲が高くつきやすい。
そもそも境界付けられたコンテキストの範囲を超えてのデプロイを強いられるのが
個人的にはこのパターンが嫌いな理由の1つである。(完全に私個人の主観)
委譲パターン
メリット
①テーブルの単独所有が可能
これも先ほどのデータ分割と一緒で、テーブルの単独所有権を実現できるのが最大のメリットである。
②
デメリット
①サービス間結合が密である
サービスコンソリデーションパターン
補足事項
最後にUAFと絡めた補足事項を記載しておきます。
今回扱ったデータの所有権は、ServicesビューにおけるInformation列の定義に役立ちます。
逆コンウェイ的にAA層で考えたデータのコンテキスト境界やデータの所有権の割り当てを
DA層やBA層に反映させるのです。
その際にUAFグリッドで十分に定義しきれていなかった、
Servicesビューにおける各サービスに対するデータの所有権をInformationビューに更新します。