前書き
SOLID原則は有名であるものの、パッケージ以上の粒度に関する設計原則である
【コンポーネント原則】6つは、あまり意識されていない印象がある。
3つの凝集性に関する原則と、3つの連携に関する原則から構成されている。
今回のその①では、凝集に関する3つの原則から見ていく。
これはモジュラーモノリスの各モジュールのまとめ方の際にも役立つ原則なだけでなく、
マイクロサービスの設計時にも役立つもの
だし、
複数マイクロサービスが毎回高頻度で同時に使われるから1つにまとめたい
チームトポロジーの型4種類のタイプうちの1つのタイプとしてマクロにチームを纏めたい
といった際とか、
さらに抽象度を上げると
ビジネスケイパビリティをいくつかの意味のある単位でまとめたい
といった際にも非常に役立つものである。
※ サービスには、1つ以上のコンポーネントが存在することを前提として書く。
私の大主張
要はアプリケーションアーキテクチャ層だけではなく、ビジネスアーキテクチャ層においても汎用的に使える設計原則である。
※ データドメインといったデータの境界付けられたコンテキストにおいても使える発想であることから、エンタープライズモデルの4階層全てに使える設計原則であると私は踏んでいる。
よって、以下のすべて原則集とその考え方は、
サービスというハードパーツレベルの抽象度だけでなく、
それ以上の抽象度である組織ケイパビリティや組織文化の変化変革観点での凝集、
再利用のまとめ方などにおいても使えるような発想であると思ってほしい。
コンポーネントの6大原則
クリーンアーキテクチャ本にも出てくる6つのコンポーネント原則がある。
まずは凝集に関する3つから取り上げていく。
閉鎖性共通原則CCP
これはSOLIDのS、単一責任原則のコンポーネント版である。
ある仕様変更の際に、同時に変更されるものを1つのまとまりとしてグルーピングするという原則。詳細は以下の記事を見ていただきたい。
この原則に反していると、1つの変更に対してあっちこっちにも修正箇所が起きていることになるので、そもそもの修正箇所の特定に時間がかかってしまう。
さらには影響範囲の特定にも時間がかかるので、結果的に素早いデリバリーは不可能となる。
理想は仕様変更の際に、そのコンポーネントだけを修正すればよく他のコンポーネントは変更しなくていい状態を目指すこと。
他のコンポーネントにも影響出てしまう時は、そこも同時に変更しなくてはならない可能性があるため、だったらそこも含めてまとめるべきと考えられる。
静的なモデル図で密な実線結合であるような箇所、
たとえば顧客と顧客の氏名などのように、
エンティティとその属性を構成する値オブジェクトのような密な関係性の所は、
この原則に従い1つにまとめられていると、仮に変更が入るような際非常に分かりやすい。
この原則は後述の残り2つの凝集原則とは違い、不確実な変更が入るという脅威への対処をしたいという欲から来るまとめ方の原則である。
よって、【枯れた技術】と呼ばれる安定したモジュールに関しては、無理にこの原則を適用する必要はない。
あくまでも不確実な変更という脅威を想定した上でのグルーピングの目安である。
サービスの粒度でCCPを適用したいケース
先に言っておくと、モジュラーモノリス時点で他のモジュールとの境界位置が曖昧という場合には、絶対にまだマイクロサービスとして分離してはならない。
そのモジュールを担当しているチームと部分的にコラボ連携して、継続的に境界の位置を探す必要性がある。
けれども、
①その他モジュールとの境界位置が曖昧ではなくハッキリしている状況下、
チームトポロジーのインタラクションで言えばXaaSモードが適用できる場合
であり、かつ
②そのモジュール内のコンポーネント同士の境界の位置が変わりやすい
または、
③モジュールレベルのマクロな仕様変更があった際、
そのモジュール内の一部のコンポーネントのみの修正ではなく、すべてのコンポーネントが同時に変更される
という時には、それらのコンポーネントはこのCCPに則って1つのサービスに凝集させる。
同時に変更されるにもかかわらず、それらがまとまってないってことは、
適切にコンテキスト境界が定義されていないってことだから。
私の主張
わたしの主張として、
エピックサーガと伝言ゲームサーガは、余程の理由がない限り採用しない。
というもがある。
なんなら極論これら2つは個人的にマイクロサービスではないとすら思っている。
疎結合で拡張性が売りのマイクロサービスのサーガパターンの一部でありながらも、
・トランザクションがアトミック整合性
・サービス同士がかなり密な結合状態であり、拡張性や応答性が低い
そんな特性を持つエピックサーガパターンと伝言ゲームサーガパターンの2つに関しては、
サービスとして分割するのではなく、このCCPに則って1つにまとめられていた方が良いという設計思想である。
よっぽどそれに勝るような他の事業でもそのコンポーネントを再利用することによる節約コストの方が勝るというんでもない限りは、エピックや伝言ゲームパターンはビジネス構造的にも採用したくない分散型ワークフローであると感じるからだ。
また案件の中でビジネスモデルのワークフローを考えている際に、
「分散型アジャイル組織でワークフロー全体を実現させたいし、データの整合性もアトミックにしたい。」という要件が顧客から言われた際に、
基本的なサーガパターンの特性を理解していれば、その時点で現実的にそれは難しいです。とかって提案が可能である。
全再利用原則CRP
これを適用したいような状況は、
だんだんと変更の頻度が落ちてきて安定性が担保されてきて、
不確実な変更という脅威へ対処したいという願望
<< そろそろ他のとこでも使いまわしたいという願望
が成立した時である。詳細な内容は以下の記事を見ていただきたい。
この原則では上記のCCPとは違って対象のコンポーネントを利用する際に、余計なものが混ざっていてほしくない。
すべて再利用するか、全く使わないかのどっちかであってほしいという纏められ方である。
なので、1つにまとめられたコンポーネント内でどれかだけ使うということはあってはならないということになる。
また、これ単体で使うというよりは、実際に再利用性を意識した設計をする際には、
後述の再利用・リリース等価の原則(REP)とセットで絡めてまとめることが多い。
まだ不確実性があるにもかかわらず、この原則にのっとってまとめてしまうのはリスクが高い。
まとめたコンポーネントがまだ品質として安定していない状態で再利用性を考える際には、変更されるリスクの大きさを考えた上で決めた方がいい。
変更されるリスクが不確実ならば、再利用性はまだ考えない方がいい。
サービスの粒度でCRPを適用したいケース
この原則もまたマイクロサービスの再利用パターンの際に、非常に重要になってくるまとめ方の原則である。
ただし、サービスコンポーネントとしてまとめる際には、この凝集原則に対して素直に則って纏めるだけでは足りない。
以下の3つの代表的なパターンの中からリスクポイントも踏まえた上でチョイスすることになる。
【共有ライブラリ】として再利用できるように設計するのか?
【共有サービス】として再利用できるように設計するのか?
【コードレプリケーション】として再利用できるようにするのか?
これらを変更リスクや強く満たしたい運用特性などを絡めてトレードオフ考察した上で、決めなくてはならない。
詳しくは以下の記事をご覧ください。
また、これはサービスコンポーネントの粒度だけではとどまらず、
そのさらに上位概念であるビジネスケイパビリティや、ビジネスポリシーに対して再利用したいといった際のまとめ方においても重要な考え方である。
たとえば
①【外部環境に合わせて価値観を素早く変える能力】
②【事業継続性の能力】
といったものは、どこの組織かに依らず汎用的に求められるようなビジネスケイパビリティである。
こういったものがバラバラに存在するよりも、いつでもセットで再利用されるようなビジネスケイパビリティに関しては、このCRPに則ってまとめられているべきである。
私の主張
ただし、その際に①と②は揃ったVerではない という場合には、
同じコンポーネント内に異なるVerのケイパビリティが混ざってしまうことになるので、後述のREPと踏まえた上でまとめるべきである。
こういった設計思想をビジネスアーキテクチャ層から取り入れておくと、
後からのメンテがしやすく【秘伝のタレ】状態を防ぐことができると感じている。
また、すでにあるリソースをなぜ再利用した方が、費用対効果が高いか?の証明にも繋がる。
ある事業Aで開発したコンポーネントを別の事業においても使いまわしたいといった際に、
このCRPでまとめた上、インターフェイス分離原則と絡めて使うといったように、
どのような設計手法が考えられ、組織構造はどうすべきなのか?という指針を与えてくれると感じている。
詳しくは上記の「インターフェイス分離原則とコンウェイ法則」記事をご覧ください。
再利用・リリース等価の原則REP
これは主に上記のCCPやCRPとセットで考えるようにしなくてはならない。
特に注意したいのが、CRPだけを考えたまとめ方のように
「このモジュールAとあのモジュールBはほぼ必ずと言っていいほど、同時に使われるからまとめてしまおう。」といったようにしてしまうこと。
AとBのVer管理が別々にされてしまうような状況下では、
互換性を保てなくなるリスクがあるため、AとBを分けるかVerを一緒へできるようにする。
そのためにこの原則が言っている方針が存在すると言ってもいい。
サービスの粒度でREPを適用したいケース
たとえば上記でのCRPのようにコンポーネントAとBをセットで毎回再利用したい
といった際に、すぐ安直にAとBを1つにまとめてしまってはならない。
AとBが別々のタイミングで変更されることで、バージョニング管理の問題が発生してしまうからだ。
ここではA、Bはビジネスサービスの粒度であると仮定しよう。
サービスAとBの要件が毎回同時に変更される
この場合には、リリースのタイミングがA、Bとも一緒になるので、
AとBを1つのサービスにまとめてしまい、その中のコンポーネントとしてA、Bを定義するという風にする選択肢も残しておきたい。
勿論その際に、AとBをまとめたことで1つにまとまったサービスの粒度サイズが巨大化し、
そのサービスは少なくとも2つ以上の責務を持ったコンテキストになるデメリットは存在する。

しかしながら、変更リスクポイントがVery LowまたはLowであれば、
多少多重責務となってしまっても、AとBを1つのサービスに凝集させた方が、メリットが上回ると感じている。
サービスAとBの要件が別々に変更される
私の主張
振り返り
今回はビジネス要件定義、ビジネスサイドのアーキテクチャを考える際にも重要になってくる設計原則を通したまとめ方に関する事例を紹介してきた。
次回の記事では、連携の仕方について触れていきます。
チームトポロジーの4種類
また今回触れたコンポーネントの凝集を通して、各コンポーネントを担当するチームは、チームトポロジーにある4種類のタイプのうち、どのタイプなのかについても組織構造を考える際、同時に考えてみると組織のアジリティを高めやすい。
それだけでなく依存関係の向きや、チーム同士の連携の仕方をどうしたらいいのか?
という既存のベタープラクティスや アンチパターンを再利用できるのでおすすめです。