2.5 SLAP
この原則は、見通しの良い保守性を担保した設計をするためには必須の原則であり、
この内容だけでかなりの分量になってしまったので、SLAP単体での記事にすることにする。
都築の記事は以下に添付する。
どういうことか
抽象度を揃えなさいという原則である。
コードのレベル感を揃えるために、抽象度の異なるものは階層を分けて(縦の関心分離)
抽象レベルを統一化するということ。
※ この原則は正直、Best3に入るほど超重要な原則
これは処理の抽象度に限らず、モジュールの粒度にもいえることである。
SLAPにしたい理由
粒度感が揃っていることによって、要約性と可読性がともに満たされる。
たとえば、この原則に違反した汚いコードは、クラスの粒度とコンポーネントの粒度が一緒のものとして扱われたりしてしまうことがある。
これは非常に読み手に取って理解がしにくい内容である。
なぜなら、目的と手段が同じ階層にある関係として扱われたりしてしまうだけでなく、
最悪、手段の方が目的の粒度よりも下位概念として扱われるリスクがあるからだ。
(例:パッケージを目的としたらクラスは手段なのに、それらの上下関係が逆転)
保守性に影響するから
階層関係に秩序がないと、目的や手段、そのまた手段の要素がすべてぐちゃぐちゃな階層構造となってしまい、影響範囲の特定などが難しくなってしまうし、短時間で泥団子化しやすい。
SLAPによって粒度感を揃えたら、具体から抽象の向きに揃えることができ、全体の見通しがしやすくなり、保守性が担保しやすい。
フェルミ推定を用いた見積もりの精度向上
これはモデリングを用いて、さらにそのモデルに対してSLAPを適用している状況と、
そうでない粒度感がぐちゃぐちゃなモデル図とでは、見積もりの精度が全然違うということ。
SLAPを意識して各コンポーネントが同じ程度の粒度感であるならば、
各コンポーネントを開発運用する人系スキルがほぼ同一であると近似的に見なした場合、
1つ当たりのコンポーネントの変更コストが、どれもおおよそ同じくらいと見なせるということ。
よって、もしも仮にあるマイクロサービスのサービス量子1つの変更が求められた場合、
そのざっくりした定量コストは、
マイクロサービス内のコンポーネント数 × 1コンポーネントあたりの変更コスト
と近似的に算出できる。
対称性の崩れた部分を浮き彫りにしやすい
これは業務プロセスモデルでも同様のことがいえる。
抽象度を意識した業務プロセスモデリングを行っておくことにより、
一部分だけやたらコストがかかるとか、対称性が崩れた部分が浮き彫りになりやすい。
仮説的に抽象度を揃えたモデルを作成後に、実際に運用してみて対称性が崩れた部分に対して、プロセスの統合や分割を行いやすくなるからだ。
この分割・統合に関しては、以下のECRS原則の記事を参照。
何を心掛ければいいのか?
関心の分離
このSLAPを満たすために意識しなくてはならないのは、関心の分離である。
ここでいう関心の分離とは、目的とその手段に対して、縦関係の関心を分離することを指している。
目的とそれに対する手段を同じ階層として扱ったらどんな不利益を被るかくらいは、容易に想像がつくであろう。
たとえば、目的は固定のまま、その実現手段のみを変えたいといった場合に、
互いに密に結びついているがゆえに、本来なら影響を受けないはずの目的までも影響してしまうなんてことが起こりうる。
逆にいうと、そのような事態が発生したら縦の階層の分離をできていないという以外の何物でもない。
コンポーネントベース分割
たとえば、以下の記事にあるようなコンポーネントベース分割は、
モジュールに対してSLAPを適用するための手法である。
ただしこれはコードベースでコンテキスト境界の位置を探っていくような、
わりと時間のかかる工程なのと、この記事で書かれているような6ステップすべてが
必要になる前に常日頃モデリングしておくことが望ましい。
名前空間をわかりやすくする
「x.a.A」「x.a.B」というようにドットで階層を区切って
Aクラスは、xコンポーネントのaパッケージ直下にあるというように、
座標をみてどの粒度の要素なのかがわかるようにすることで、粒度がぐちゃっとなることを予防できる。
詳しくは上記の記事にある名前空間設計を参考にしてください。
単一責任原則
責務が1つに絞られていないと、どんどん粒度感が膨れ上がりやすくなり、
SLAPに反しやすくなってきてしまう。
ある程度変更頻度が落ち着いてきて、再利用性を考慮したくなってきたら、
場合によっては、そのモジュールに対して複数の責務を持たせたくなってくるときもある。
その際に、再利用というメリットを優先するのか?
それとも粒度感が他のモジュールと不揃いになりメンテ性が低下することを許容するのか?
それを天秤にかける必要性はある。
また、パッケージ以上の粒度感のコンポーネントに対しては、
コンポーネントの凝集原則である、閉鎖性共通原則(CCP)に則った設計が求められる。
モデリングによるトップダウン手法
モデリングを使ったトップダウン手法は仮説ベースなところがあり、
実際のコードを使って運用してみて、戦術的フォークやらコンポーネントベース分割を
用いたボトムアップ手法ととの組み合わせによってよりSLAPを実現できる。
私がモデリングで事業全体のマクロなコンテキストマップを描くことを推奨しているのは、
そのためである。具体的な手法の手順としては、以下のステップに分解される。
①コンテキストマップを描く
まずは事業全体というマクロなままではなく、1段階詳細なコンテキストに分割する。
それをストラクチャー図にすると以下のような階層関係だ。

このときに、実際にこうモデリングでこのABCは粒度的には一緒でしょと思っても、
それはあくまでも仮説の範疇を超えないため、この段階でのモデリングは、
「事業全体は、A・B・Cというサブ領域に分解できるよね」
「逆にA・B・Cというサブ領域を足し合わせたら事業全体になるよね」
というループバックチェックを行い、さらに
「AはBに含まれることはないか?」という問いかけを通して、OKならこの時点での
コンテキストマップとして下図を定義して、このステップはいったん終わり。
②さらに1段階分割してみる
①と同様にして、たとえばコンテキストAなどを分割してみる。
同じように他のBCに対しても分割してみる。
そして、他のAをさらに分解したサブサブコンテキストたちと粒度が揃ってるかの検証をする。これを自分たちの業務理解が進むレベルのコンポーネントになるまで繰り返す。
さすがに最小単位であるクラスのレベルまで分解するのは、やり過ぎ感がある。
③下位の結果を上位階層に反映させる
徐々に詳細な段階まで分割していくと、自分たちの解像度も上がっていくので、
モジュールの粒度が揃っているのかの検証が行いやすくなってくる。
詳細で検証し、仮にどこかで粒度が不揃いであることが発覚したら、
それを上位に反映させて、階層関係を調整する。
ただし、モデルの段階では机上の空論を超えないため、後は実際にコードを書いてみての
検証活動になる。
④コードを書いて検証する
実際に動くコードを書いてみることで、自分たちの定義したコンテキスト境界の位置の検証もでき、かつモデルという仮説上の階層構造の検証活動もできる。
この時に重要な考え方は、モデル作成が仮説構築段階。
コードを書いてそのモデルの妥当性を検証するのが、仮説モデルの検証段階であり、
常に「そのモデルは間違っているかもしれない」というクリティカル思考を持つこと。
⑤仕様変更による粒度の変化を監視する
④までのフェーズ中は、いったん仕様変更などの外的力学を加えることを意図的に止める必要がある。そして一度定義してSLAPに従った構造を定義したつもりでも、仕様変更などによって容易に形を変えるため、継続的に考え続けなくてはならない。
仕様変更によって徐々に、最初はABCそれぞれのコンテキストが同じ粒度で扱われていたとしても、徐々にAコンテキストにだけ拡張が頻繁に入り、どんどん中身の要素数が膨れ上がってきたとしよう。
それが続くと、AはBやCと同じ粒度として扱うことはできなくなってくることは感覚的にわかるであろう。
それを定量的にチェックする方法が、コンポーネントベース分割パターンにある手法だ。
また、戦術的フォークによって定期的に不要なコードを消すことも挟んだ方がいい。
⑤ステップのまとめ
モデルで仮説を作成しつつ、コンポーネントベース分割や戦術的フォークによって
動くコードベースでそのモデルの階層構造を検証し、SLAPを保つ。
ただし、物事はちょうどいい塩梅というものがあるためその注意点を以下に書く。
注意点 -やりすぎない-
なんでもSLAPにしたらいいってもんでもない。
完璧にSLAPを意識した設計なんてやってたらお金がいくらあっても足りない。
モデルを描き、コードを書いてみないことには妥当性が不明という不確実性が高い場合に、
もうモデルを描いた時点で「こことこのモジュールってコードを描くまでもなく粒度一緒だよね。しかもあまり変更はいらないよね。」という部分に関しては、あまり細かいコンポーネントに分割せずともいい。
あくまでもモデル図で粒度感の共通認識が対話できるレベルでいい。
他の原則との関係性
KISSとの関係性
粒度を揃えることによって、構造がシンプルになり、全体感の見通しが良くなる。
結果的にSLAPを意識することは、KISSを満たすことにも繋がる。
これはデータアーキテクチャ全体の地図をどうサブジェクトに切り分けるのか?
という考えをする際にも重要な考えである。
1つだけやたらサブジェクトの粒度感が大きい、
たとえば、他のサブジェクトはテーブル数が20程度なのに、あるサブジェクトだけ200テーブルとかは、他のサブジェクトと粒度が揃っていないかもしれないというシンプルでない構図を生みやすい。
PIEとの関係性
PIE原則を満たしていないと、SLAPは満たしにくくなってきやすくなる。
これはもう体感的にわかるのではなかろうか。
その処理だったり、モジュールまたはコンポーネントの意図をつかみやすいと、
その要素がどのコンポーネント直下に置かれるのか?とかが類推しやすく、
SLAPを満たしやすくなる。
抽象・安定依存の原則
コンポーネントの結合原則にある「安定に依存せよ」という原則とSLAPを組み合わせることで、世間で言われているクリーンアーキテクチャの設計の基本が出来上がる。
また、それはエンタープライズアーキテクチャモデルでもいえることである。
最上位のビジネスアーキテクチャ←次のデータアーキテクチャ←アプリケーションアーキテクチャ←テクニカルアーキテクチャ← という矢印の向きに依存している。
さらに各アーキテクチャ層では抽象度が揃っている。
それら2つの条件が最低揃って、優れたエンタープライズアーキテクチャといえるであろう。
要求モデル×SLAP
SLAPは何も設計上だけでのみ意識すればいいというものではない。
本には書かれていないものの、要求の抽象度に関しても揃っている必要性がある。
SLAPは要求分析ツリーにも適用した方がいい
事業戦略レベルの要求なのか? それともそれを実現する事業戦術レベルの要求なのか?
あるいは、その戦術を構成するために用いられるシステムの要求なのか?
それらを図のように抽象度を揃えて見やすくしておくだけで、
どの要求に選択集中して実現すれば、全体の要求の系が実現できるのか?
つまり、事業に関わるほとんどのステークホルダーを満足させられるのか?を非常に考えやすくなるだけでなく、ある抽象度のステークホルダーの要求が変化した際でも、
どの範囲に対して影響が出てしまうのか? といった分析が非常にしやすい。
逆に適用しないとどんなことが?
ステークホルダーの種類がかなり少ないとかで、要求自体がかなり少ないといった場合には、特にこの要求モデルにSLAPを適用する恩恵はさほど感じることができない。
ただし、ステークホルダーの種類が少しでも増えてくると指数関数的に適用しない場合の代償が跳ね上がる。
要求分析ツリーに適用しない場合に関しては、まず要求同士の適切な因果関係が定義できにくい。そのため、どの要求を選択集中して実現したら全体の系が実現できるのか?の分析を見誤る可能性が大幅に上がる。
しかもそれは後工程のアーキテクチャを考えるところにも当然悪影響を及ぼし、
どのような品質特性を優先的にアーキテクチャに適用しなくてはいけないのか?
という判断も間違うことに響く。
SLAPを他の領域に適用した例
ステークホルダーの粒度感
ステークホルダーマッピングの際に、粒度感が不揃いな場合には、
要求開発やその要求の関係性の変化の運用などの際に、非常に面倒くさくなってしまう。
詳しくは、以下の記事を参照ください。
チームトポロジー×SLAP
チームの粒度感においても、この原則を意識した設計が行われていることが望ましい。
あるチームは2つや3つの責務を担っているのに、
他のチームはたった1つの責務を担っているといったことが、
複雑に混在している場合には、ある意味組織内のチームコンポーネントの粒度が
統一されておらず、しかも認知負荷の量もばらばらである可能性が高い。
あえてデメリットを承知で、そうしているのなら例外であるが、
基本的には、まずはこのSLAPを意識した組織設計を目指すことをお勧めする。