😎

【WHYクリーンアーキテクチャ?#1】全てのエンジニアが知るべきシステム設計の基本(凝集度, 結合度, 安定度)

に公開

はじめに

「このコード、変更するのが怖い…」
「新しい機能を追加したいけど、どこに手をつければいいか分からない…」

多くの開発現場が、このような「変更に弱い」システムに悩まされています。

その根本原因は、システムの設計にあります。

クリーンアーキテクチャは、こうした問題を解決し、長期的に価値を提供し続けるための設計思想です。

その核心は、ビジネスの本質的なルールを、フレームワークやデータベースといった「技術的な詳細」から守ることにあります。

今回は、なぜクリーンアーキテクチャがソフトウェア設計において不可欠なのかを、マクロな視点(この記事)・ミクロな視点(#2)を2章に分けて解説していきます。

この記事では、なぜクリーンアーキテクチャが必要なのかという意義をマクロな視点から解説し、その根幹をなす「依存性のルール」、そしてそのルールを支える「凝集度」「結合度」「安定度」といった基本原則までを解説します。

この記事を読めば、良い設計と悪い設計を見分ける「ものさし」を手に入れることができるはずです

クリーンアーキテクチャが目指す「たった一つのルール」

そもそも、変更に弱いシステムはどのように生まれるのでしょう?

それは、あるモジュールの一部分を変えたら、予期しない別のモジュールに影響が波及するような場合です。

じゃあ、なぜそのようなあるモジュールを変えたら、別のモジュールにも影響が及んでしまうのでしょうか?

それはズバリ、依存関係が生まれているからです。

「依存性のルール」

だからと言って全ての依存関係を無くしてしまうと、モジュール同士が繋がらず、システムとして成立しません。

問題は依存の有無ではなく、その向きにあります。

そこで登場するのが、クリーンアーキテクチャが提唱する 依存性のルール(The Dependency Rule) です。

「ソースコードの依存性は、必ず「内側」に向かわなければならない」

これは一体どういう意味でしょうか?

  • 内側(安定した中心):
    • ビジネスの本質的なルールやロジック。これらは、例えば会社の事業が変わらない限り、滅多に変化しません。(例:「ユーザーは注文できる」というルール)
  • 外側(不安定な末端):
    • フレームワーク、データベースといった具体的な技術要素。これらは時代と共に変化する可能性があります(例:Ruby on Rails, MySQL, Reactなど)

このことから、「依存性のルール」とは、 「ビジネスルールは、フレームワークやデータベースといった技術的な詳細に依存してはならない」 という、依存の向きを厳格に定めたルールなのです!

このルールを守ることで、例えば「データベースをMySQLからPostgreSQLに替えたい」と思っても、ビジネスロジックに一切影響を与えることなく変更できます。なぜなら、ビジネスロジックはデータベースのことを何も知らない(依存していない)からです。

このように、システム設計とは「依存関係をいかに適切に管理するか」という問いであり、クリーンアーキテクチャは 「依存の矢印は、常にビジネスの中心へ」 という明確な答えを私たちに示してくれます。

ルールを支える「3つのものさし」

実は、この依存性のルールを実現するために不可欠な3つの道具(ものさし)があります。

尚、今後システム設計を深く学んでいくにあたって、この3つをハッキリと理解していることが大前提となるので、ここで必ず抑えておきましょう。

凝集度

ものさしの1つ目は、「凝集度」です。

凝集度とは「各モジュール内のまとまり」に着目しています。ここでいうモジュールとは、「ビジネスの本質」と「技術的な詳細」のそれぞれの層の中での単位です。

なので、凝集度は、依存性のルールを実現するために依存を調整するための下準備にあたるプロセスです。

凝集度の鉄則:1つのモジュールに1つの関心事

各モジュールのまとまりを良くするためには、モジュールが扱う関心事が1つだけであるようにすることが一番重要になってきます。

つまり、1つのモジュールが複数の関心事を抱えていたら、そのモジュール内での変更が、本来1つだけの関心事であったものが、複数の関心事に跨いで影響を与えてしまいます。

このような、1つのモジュールが複数の関心事にまたがってしまっている状態を、「低凝集」 と表現します。

一方で、1つのモジュールが1つだけの関心事にまとまっている状態を 「高凝集」 と表現します。

ものさし 2 & 3: 依存関係の「強度」と「向き」をデザインする

さて、モジュール毎のまとまりを整えた後は、晴れて依存を実際に扱っていきます。

それじゃあ、どのような依存の状態が理想なのでしょう?

それは 「結合度が低く(疎結合)、かつ依存の矢印が安定度の高いモジュールへと向かっている」 状態です。

上記を前半・後半に分けて解説していきます。

ルール1:依存の「強度」を弱める(結合度)

最初のルールは、モジュール間の依存の「強度」、すなわち結合度を可能な限り弱めることです。

これは、人間関係でいう「癒着」と言い換えることもできます。

結合度とは、あるモジュールが、別のモジュールの内部をどれだけ知っているか、その度合いを示す指標です。

この結合度が高い(=癒着がひどい)と、変更に非常に弱いシステムが生まれます。

密結合が引き起こす「悲劇」

レストランの厨房を例に考えてみましょう。

ウェイターがシェフに、「A社の卵黄を使ってカルボナーラ1つを作れ」と、シェフの業務(実装)に直接口出ししているとします

これが密結合の状態です。

この時、どんな悲劇が起こるでしょうか?

  • 悲劇1:シェフが改善できない
    シェフ係が「もっと美味しいB社の卵黄を見つけた!」と思っても、ウェイターの指示を変えない限り、卵黄を変えることができません。

    厨房(シェフ)内だけで完結するはずの小さな改善が、ウェイター側のシステムの都合によって縛られてしまっている状態、まさにウェイターとシェフが「癒着」した状態です。

  • 悲劇2:ウェイター・シェフが再利用できない
    お店が新たにシェフを雇ったとしても、ウェイターのA社の卵黄を使ってカルボナーラを作るよう支持することに納得しているシェフでなければ、ウェイターとシェフとの間で怒鳴り合いが始まります(バグの発生)。

    これは、ウェイター・シェフの両者のモジュールの再利用性が著しく低下している状態です。

結合度のレベルを知る

このように、密結合はシステムの柔軟性を奪います。

では、どのような結合が「密」で、どのような結合が「疎」なのでしょうか。代表的なものをいくつか見てみましょう。

  • 【強】内部共有結合(最悪の癒着)
    • モジュールAが、モジュールBのデータベースのテーブルを直接参照したり、プライベートな変数を直接書き換える状態。ウェイターがシェフの休日の手料理にまで口を出してきているような状態です。
  • 【中】制御結合
    • モジュールAが、モジュールBの内部の振る舞いを制御する情報(フラグ)を渡す状態。先ほどのオーダーだけでなく卵の指示もするウェイターのように、相手の行動を細かく指示するマイクロマネジメントのような関係です。
  • 【弱】データ結合(理想的な関係)
    • モジュール間で必要なデータだけを引数として渡す状態。これはウェイターが「カルボナーラを1つ」と、必要な情報(注文内容)だけを渡すインターフェースを通じた連携です。

このように、私たちの目標は、モジュール間の関係性をできるだけ「データ結合」に近づけ、お互いが「何をするか」だけを知っていればよく、「どうやるか」は知らなくてよい、という疎結合な状態を作り出すことなのです。

ルール2:依存の「向き」を正す(安定度)

2つ目のルールは、依存関係の「向き」を正しく定めることです。

そのコンパスとなるのが安定度という「ものさし」です。

安定度とは、そのモジュールが 「どれだけ変更されにくいか」 を示す指標です。

安定度が高いモジュール:
変更される理由がほとんどない、システムの本質的な部分。(例:ビジネスルール)

安定度が低いモジュール:
人間の都合や技術の流行り廃りによって、頻繁に変更されうる非本質的な部分。(例:UIデザイン、特定のデータベース製品)

そして、クリーンな設計には、依存の向きに関する絶対的なルールが存在します。

それが 「安定依存の原則(The Stable Dependencies Principle)」 です。

依存の矢印は、不安定なモジュールから、安定したモジュールへと向かわなければならない。

言い換えると、 「変わりやすいものは、変わりにくいものに依存せよ」 ということです。

レストランを例に考えてみます。

レシピ(安定):

「カルボナーラのレシピ」という、店の根幹をなすビジネスルール。一度決めたら滅多に変わりません。 安定度は非常に高いです。

特定のフライパン(不安定):

「A社のテフロン加工フライパン」という、具体的な調理器具。もっと良いものが見つかれば、すぐに買い替えるかもしれません。 安定度は非常に低いです。

間違った依存の向きが引き起こす「厨房の大惨事」

ここで、もし 「レシピが、特定のフライパンに依存する」 という間違った向きの依存が生まれたら、大惨事が起こります。

レシピ:「まず、A社のテフロン加工フライパンを2分間、中火で熱する…」

シェフが「もっと火力の強いB社の鉄製フライパンを導入しよう」と決断した瞬間に、レシピそのものを全て書き直さなければならなくなります。

これが、不安定なモジュールに依存した結果の悲劇です。

正しい依存の向き

上記の場合、依存の向きが逆であれば、優れた設計となります。

フライパンという 不安定で具体的な「詳細」が、レシピという安定的で抽象的な「方針」 に依存するのです。

こうすることで、私たちはフライパンを自由に買い替えても、レストランの根幹であるレシピに一切影響を与えることなく、ビジネスを継続・改善していくことができます。

私たちの目標は、この厨房のように、ビジネスの本質(安定)が、技術的な詳細(不安定)の移り変わりに一切影響されない、しなやかで強いシステムを構築することなのです。

おわりに(まとめ、次の話)

この記事では、変更に強いシステムを支えるクリーンアーキテクチャの「魂」となる「依存性のルール」について解説しました。

そして、そのルールを実現するために、私たちエンジニアが使うべき3つの「ものさし」――凝集度・結合度・安定度――を学びました。(❗️必ずこれらの役割を、自身の言葉で説明できるようにしておいてください❗️)

もう、あなたは目の前のコードが「なぜ変更しにくいのか」を、これらの「ものさし」を使って論理的に説明できるはずです。

良い設計と悪い設計を見分けるためのコンパスを手に入れたのです。

しかし、哲学とコンパスだけでは、まだ道は進めません。

「ビジネスルールを、技術的な詳細から守る」という哲学を、私たちは一体どうやって実際のコードに落とし込めば良いのでしょうか?

次回、第二章では、いよいよクリーンアーキテクチャという名の「設計図」を広げます。

ドメインモデル、ユースケース、インフラストラクチャといった具体的なレイヤーが、この依存性のルールをいかにして実現しているのか、その巧妙な仕組みをミクロな視点から解き明かしていきましょう。

Discussion