マイクロサービスにおける単一責任の原則

動機

前回の動機で書いたように、マイクロサービスアーキテクチャには凝集性はマイクロサービスを考える際に重要な概念であり、SRP(単一責任の原則)の定義によって認識が強化されたと記載されていました。その概念はマイクロサービスを設計していく上で重要になりそうなので、しっかりと理解したいと思います。

凝集性とは

凝集性の定義を確認します。意味はわかるのですが言葉にするのは難しいです。ウィキペディアによると、凝集度(ぎょうしゅうど、コヒージョン、cohesion)とは、情報工学においてモジュール内のソースコードが特定の機能を提供すべく如何に協調しているかを表す度合いである。とのことです。
ウィキペディアの高い凝集度に記載の説明、凝集度は、あるコードがどれだけそのクラスの責任分担に集中しているかを示す尺度である。オブジェクト指向プログラミングでは、クラスの凝集度を高めるようにそのクラスの責任範囲を設定することが有益とされている。凝集度の高いシステムでは、コードの読みやすさと再利用の容易さが増し、複雑さが管理可能な程度に抑えられる。がわかりやすいかなと思います。この説明で重要だと思う点はクラスの凝集度を高めるようにそのクラスの責任範囲を設定することが有益という点です。これは責務駆動設計であり、単一責任の原則に従うべきであるということです。責務駆動設計についてはオブジェクトデザインが参考になると思います。この本は10年くらい前、まだオブジェクト指向がしっかり理解できていなかった頃に読んで、とても参考になりました。みなさんもある処理を追加するときに、この処理はどのオブジェクトに行わせるのがよいのかを考えると思います。それが責務駆動設計です。

単一責任の原則について

単一責任の原則について、再度確認したいと思います。

アジャイルソフトウェア開発の奥義によると、

クラスを変更する理由は1つ以上存在してはならない

と記載されています。これだけを見るとどういうことかわかりづらいですし、しかもそんなこと可能なのか?と思ってしまいます。実際には、クラスを変更する理由はただ1つであるように設計するということです。なぜそうすべきなのでしょうか。

単一責任にすべき理由

アジャイルソフトウェア開発の奥義にはいくつか単一責任にすべき理由がかかれていますが、正直あまりわかりやすいものではないと思います。
わたしは、単一責任にすべき理由はただ1つで依存関係を減らすということだと思います。

これは静的言語で特にいえることだと思うのですが、変更したクラスに直接依存しているクラスは再コンパイルが必要です。クラスが複数の責務を持ち、いろんなクラスと依存関係を持っている場合、ある一つの変更で、依存関係を持っているクラス全てを再コンパイルしないといけません。再コンパイルしたプログラムはさらにデプロイを行わなければいけません。
これは静的言語だけではありません。わたしが業務で関わっているRailsでも複数の責務を持つクラスを変更すると、そのクラスに影響する全てのクラスのspecを実行しなければなりません。その数は少ない方が良いです。

変更する理由を1つにするということは、関係のないクラスの再コンパイル、デプロイが発生しないということだと思います。プログラマが知るべき97のことで、ロバート・C・マーティンは、良いシステムデザインとは、システムのコンポーネントがそれぞれ独立してデプロイできるようになっているデザインのことですと言っています。まさにその通りだと思います。
ただ依存関係を持たずに単独で仕事をするクラスはあまりありません。ということは、結局あるクラスを変更すると、依存関係を持っているクラスも再コンパイル、デプロイが発生するのではないか?という指摘があるかもしれません。その場合は依存関係逆転の原則を利用します。わたしがSOLID原則の中で一番好きな原則です。また別の機会で詳しくみていきたいと思います。

マイクロサービスにおける単一責任の原則の応用

では、マイクロサービスの設計において、単一責任の原則はどう考慮していくのが良いでしょうか。
先ほどデプロイの話が出てきました。あるサービスを変更したときに、依存関係を持っているサービスが複数あると、それらを同時にデプロイしなければいけません。それっは不可能ではないかもしれませんが難しいことだと思います。それを防ぐために単一責任の原則を守っていくべきだと思います。

しかしそれでもプログラムと同じような問題は発生します。サービス間のインターフェースなどを変更してしまうと、依存関係を持っているサービスも変更せざるを得ません。それを防ぐためには依存関係逆転の原則をサービス間に適用します。サービスは他のサービスに依存するのではなく、サービス間のインターフェースに依存するようにします。そうすれば、各サービスの変更を行っても他のサービスは変更を行わなずにすみます。

まとめ

単一責任の原則をマイクロサービスの設計にどう活用するかをみてきました。マイクロサービスは以下の二つのことを守れば、他のマイクロサービスの変更の影響を受けずにすむでしょう。

  • サービスの凝集度を上げ、責務をはっきりさせる。
  • サービス間はサービスに依存するのではなく、サービスのインターフェースに依存するようにする。

マイクロサービスを設計するときには気をつけていきたいと思います。