Clean Architectureは幻想的
Last Updated on 2023年3月9日 by lemonade
Clean Architectureの解説が主で、幻想的で好きですって話です。
Clean Architectureとは
変更に強いソフトウェアを作るためのコツみたいなものです。
変更に強いソフトウェア?
ソフトウェアって、ハードウェア(PCの電子回路とかその辺)みたいな一度作りあげるとほとんど交換以外で変更が出来ないようなものでは融通が利きづらいから融通が利くようにプログラム(ソフトウェア)の層を作って融通がある程度利くようにしようっていう意味らしいです。
だからソフトウェアは変更しやすいものでなければ本来の意味をなしていないことになります。
ソフトウェアにおける変更がしにくいとは?
では、ソフトウェアはなぜ変更がしにくいんでしょうか。それは変更すると連動して挙動が変わってしまう部分があるからです。
例えば、みんなよく作りがちなTodoアプリ。Todoアプリのタスクが現れるときにアニメーションを使用したいという要求が出た時にすぐに変更できますか? まぁ人によるかもしれませんが割と簡単ですよね。ではタスクに対して有効期限を設定し、超過したものは表示しないことは簡単ですか? おそらくアニメーションを加えるよりは簡単ではないと思われます。
この簡単か簡単ではないかの違いはなんでしょうか。別に私は技術的に難しいとか難しくないとかいう話はしません。
ひとつの要因として、依存関係が挙げられます。
タスクが現れる際のアニメーションの挙動を変更するとタスクが消えたり、他人のタスクが見えてしまったりするようなバグが入り込むことはありますか? ない、とは言い切れませんが、可能性としては薄いですよね。
なぜならばタスクの出現UIアニメーションに、タスクの有無は関係ないからです。
言い換えるならば、タスクの有無などはタスクのUIに依存していません。
一方で、有効期限の設定には、途中でタスクが消えてしまったり、見えるはずのないタスクが見える可能性があります。タスクのUIも、もしかしたら変わってしまうかもしれません。
これは、有効期限による表示ロジックには、タスク表示に関わる多くの物が依存しうるからです。
安定度が高いとは
ここで、安定度という尺度が存在します。
安定度とは、コンポーネントやクラスなどの変更のしにくさを表す尺度でもあります。
安定度が高いということはどういうことでしょうか。
それは、多くのクラスやコンポーネントから依存されていて、他のクラスやコンポーネントにあまり依存していないことを指します。
多くのクラスやコンポーネントに依存されていると、変更理由が少なくなり、変更される可能性が減ります。これは単一責任原則に由来します。
単一責任原則の幻想
あるコンポーネントの変更理由はただ一つ(のアクター)でなければならない、という単一責任原則なんてものがあったりします。
ここで(Clean Architectureの本ほど)深く説明はするつもりはありませんが、要は全く同じ意味を持たない限り共通化は行ってはならないという原則です。
少し、苦手意識があるかもしれませんがクラス図を使って説明してみます。
BとCの機能はAという共通モジュールを使用しています。このとき、Aを変更する場合にはBでもCでも変更の必要があるときでなければならないということです。
ここでもし、BではAでの処理内容を変更する必要があるけれど、CはAの処理は今のままがいい、なんてことが起こったら面倒なことになります。
これはなぜ起こりうるのでしょうか。それはBにとってのAの意味と、CにとってのAの意味が根本的に違うからです。
このようなことが起こるのであれば、最初からたとえ中身が同じであろうとAを共通モジュールとせず、分離しておくべき。これが単一責任原則です。
ステークホルダーとかアクターとかの小難しい話は本を読んでください。
この原則が幻想なのは、Aの意味を認識できない点にあります。BにとってのAとCにとってのAは本当に一緒なのか、それとも違うのか。一番わかりやすい指標がコードが同一かどうかであるため、どれだけドメイン知識への理解を深めても開発者には結局判断がつきません。経験で多少克服はできるかもしれませんが、完全には無理でしょう。だからこれは幻想なのです。
安定度が低い
単一責任原則からいって、他のコンポーネントやクラスにあまり依存されていないことが安定度が低い要因になるのはわかりますね。変更する理由を合わせる必要がなくなるからです。
もう一つは、他のコンポーネントやクラスに多く依存することです。
変更する理由の一つに依存先の変更があります。これは依存先が変更すれば必ず変更することになるため、理由のすり合わせは不要です。
先ほどの例を使いましょう。
この場合、Bに変更が発生しても、AやCには関係がありません。
しかし、Aに変更が発生すると、BとCも変更することになります。
Aの変更する確率は低いですが、その分Aが変更するときには影響範囲がBとCを巻き込むことになります。
なので、BとCは安定度が低いと言えます。
明確な安定度の測り方などや、依存先の多さと依存される多さの関係性に関しては、結構面白いのでClean Architectureを実際にお読みください。
Clean Architectureは依存の方向性の制御
ここまでから、安定度が高ければ変更しづらく、安定度が低ければ変更がされやすい。そういうことがわかりました。
では全てのコンポーネントやクラスの安定度が低ければ、変更に強いシステムが作れるのではないかと思いませんか。
結論から言えば現実的に不可能です。誰も安定度がすごく高いStringやIntに依存することなくシステムは作れませんし、作るべきではありません。システムはモジュールやコンポーネント、クラスの依存が連なって構成されているのですから。
ただ、安定度の高さの優先順位はある程度制御が可能です。
特定のモジュールの安定度を高くして他の変更の影響を受けにくくし、特定のモジュールの安定度を低くして、変更しやすくする。
これが私的にはClean Architectureの真髄です。
具体的なClean Architectureの図にいきましょう。「Clean Architecture, Robert C Martin著 p.200」の図です。
おそらくよく見る図だと思います。
この図の意味は、依存関係の方向性を指しています。エンティティは何にも依存せず、ほとんど全てから依存されるため最も安定度が高くなっています。
ユースケースはエンティティにしか依存せず、エンティティの次に安定度が高くなっています。
逆に、外周のウェブ・デバイス・DB・UIのようなものは非常に安定度が低くなっています。
このように、安定度の方向性と安定度の高さを制御することがClean Architectureです。
エンティティとは、「最重要ビジネスルール」を指します。Clean Architectureでは、ソフトウェアで作られるサービスの本質は時間が経っても変わらないという考え方のため、エンティティはほとんど変更されないことを前提に最も高い安定度に設定されます。サービスの本質が変わってしまってはそれはもう別のアプリケーションと言えるでしょう。
ユースケースとは、「アプリケーション固有のビジネスルール」のことです。サービスの本質でないものの、アプリケーションという形に落とし込む上で発生したビジネス要件を指すため、アプリケーションの操作などが変わらない限りは安定していると考えられています。
フレームワークや外部ツールなどは最も外の円に設定されています。外部サービスは本質に含まれず、変更されやすい事象と考えられています。
では実際に依存制御の統制自体はどうするのかというと実装面ではここでは説明しません。ただ、解決策は溢れています。ヘキサゴナルアーキテクチャやオニオンアーキテクチャなどもClean Architectureとあまり変わらないので参考にしてみてください。
Clean Architectureは幻想的
実際に使ったことは結構ありますし、使い勝手のいいアーキテクチャです。
これの幻想的な要因は、エンティティは変化するということですね。
現実的に言えばサービスの本質を誰も理解していないということでしょうか。
たとえ個人開発で自分がオーナーであろうと、依頼主であろうと、社長であろうと、エンジニアであろうと、サービスの本質は理解していません。作って使っていく中で新たな本質が次々に出てくるのです。
エンティティの変更は、すごく大変です。それこそ場所によっては作り直しと大差ない程度に。
なのにも関わらず開発当初はエンティティと本質が合わないことが多いためにエンティティの変更を割と頻繁に強いられます。それこそ外円の部分と変更頻度があまり変わらないレベルで。
でもきっと、長年かけて、サービスが成長していくにつれてエンティティが本質に近づくにつれてエンティティがだんだん変更頻度が小さくなり、変更要求は外円部分ばかりになって、変更容易性の高い保守性の高いシステムを作ることができるのでしょうね。それまでずっとエンティティを本質へ近づけるための変更努力をし続けることができれば、ですけど。
幻想的で超素敵
参考文献
Clean Architecture: Robert C Martin著