ソフトウェアアーキテクチャについての個人的なまとめ
Motivation
Clean Architecture で社内で共有する必要が出てきたため、自分の考えをまとめておく。主に Clean Architecture で書籍を読んで思ったことをまとめつつ、最近の潮流 (関数型、Rx、Flux) も取り入れて紹介していく予定 (できてない)。
なぜソフトウェアアーキテクチャが必要か
ソフトウェアアーキテクチャの目的
ソフトウェアアーキテクチャを導入することの目的は、ソフトウェアシステムの開発 ・デプロイ ・運用 ・保守を容易にすることである。優れたアーキテクチャは、変更に強く、拡張や修正が容易で、複数人での開発も進めやすい。
一方、これらを難しくする最大の要因は、ソフトウェアシステムの結合である。たとえば、全てのコードが密に絡み合ったモノリシックなシステムには、例えば以下のような問題がある。
変更に弱く、少しの変更のために大量のコードの書き換えが必要になったり...
分離されていないため、複数チームで開発する際に分担するのが難しかったり...
新規参入者が理解するのが困難なほど複雑になっているため、保守できなかったり...
依存が強すぎるために、テストが書きにくかったり...
関心の分離
モノリシックなシステムから脱却し、保守しやすいシステムを構築するためには、適切なソフトウェアアーキテクチャの決定が必要となる。ソフトウェアアーキテクチャの根底にあるのは、関心の分離である。特に、プレゼンテーションとドメインの分離 (PDS: Presentation Domain Separation)を遵守しやすくする。ここでプレゼンテーションとはデータの描画や入出力、ドメインとはデータ自体の加工や保存のためのロジックを指している。これらを分離すると、以下のようなメリットがある。
ドメインロジックとプレゼンテーションロジックが理解しやすい
1つのロジックに対し異なるプレゼンテーションをコードの重複なく提供できる
プレゼンテーションはテストしづらいので、ロジックはテストしやすい場所に持っていく
API をサービスとして公開したい場合に容易に行える
プレゼンテーションコードはドメインロジックを記述するのとは別のスキルが必要となる。これらを分離しておくことで、異なるスキルセットの開発者が別々に開発を行いやすい
プレゼンテーションとドメインの分離は、最も基本的な分離方法になる。関心の分離は、関心事として何に着目するか、によって様々な流派に分かれる。他の有名なアーキテクチャとしては、階層構造であるレイヤードアーキテクチャや、GUI アプリケーションでよく用いられる MVC, MVVM 等がある。また、その中でもさらに様々な流儀に分かれることもあり、どれが最も良い、とは言えない。
Clean Architecture -優れたソフトウェアアーキテクチャとは何か-
方針と詳細
ソフトウェアには、以下の二種類の価値があるとされる。
振る舞いの価値
構造の価値
ソフトウェアアーキテクチャは、後者をサポートする。ソフトウェアアーキテクチャは、システムの振る舞いにはあまり影響は与えない。アーキテクチャが酷くても正しく動いているシステムはたくさんある。しかし、そのようなシステムは大抵、デプロイ・保守・運用で問題が生じている。ソフトウェアアーキテクチャはシステムに対して適切な構造を与え、そういったシステムのライフサイクルをサポートする。アーキテクチャが正しい振る舞いをサポートしないわけではないが、サポートしたとしても、その効果は飽くまで副次的なものである。
ソフトウェアアーキテクチャがシステムのライフサイクルをサポートする戦略とは、できるだけ長い期間、できるだけ多く選択肢を残す ことである。この時に 残すべき選択肢 とは、ソフトウェアにとっては重要ではない詳細、すなわち 技術的な詳細 である。
あらゆるソフトウェアシステムは、「方針」と「詳細」の2つの要素に分割できる。方針とは、ビジネスのルールや手順を含んだ、システムの本当の価値を指す。詳細とは、方針についてやり取りするために必要なものだが、方針の振る舞い自体には影響を与えないもので、例えば、データベースとして何を使うだとか、開発言語が何かだとか、などである。優れたアーキテクトは、詳細とは無関係に方針を構築していくことで、技術的な詳細の決定を先送りにできる。これらを先送りにすると、以下のような良いことがある。
詳細の決定を延期、留保すると、詳細を適切に作るための情報が多く手に入る
例えば、方針が固まってくることで、複数の実験をして詳細を決める、ということができる
選択肢を残せる期間が長ければ、実験できる回数が増え、挑戦できることが増える
Clean Architecture 達人に学ぶソフトウェアの構造と設計 - 第 15 章 アーキテクチャとは?
境界線を引く
ソフトウェアシステムを「ソフト」に保つためには、詳細を先送りできるような構造にすることが重要、という話をした。これを実現するためには、ソフトウェアのある要素から、その詳細を切り離す必要がある。ソフトウェアの要素を分離し、お互いのことがわからないように制限することを指して、境界線を引くといった言い回しをする。
境界線を引くということは、境界線を挟んだソフトウェア要素について、お互いのことを知らないように制限するということを指す。この制限を具体的に実現するためには、SOLID 原則における OCP や DIP が活用できるが、それについては後述する。
境界線は、重要なものと重要ではないものの間に引く。例えば、GUI はビジネスルールにとって重要ではないので、その間に境界線を引く。同様に、データベースはビジネスルールにとって重要ではないので、その間にも境界線を引く。
あるいは、境界線は、変更の軸があるところに引く。境界線を挟んだ要素同士は、それぞれ変更の頻度や理由が違っている。GUI はビジネスルールとは異なる時間や頻度で変更されるので、その間に境界線を引く。ビジネスルールはフレームワークと異なる時間や理由で変更されるので、その間に境界線を引く。
Clean Architecture 達人に学ぶソフトウェアの構造と設計 - 第 17 章 バウンダリー: 境界線を引く
SOLID 原則
ソフトウェアシステムの分離に関する原則として有名なものに、SOLID原則 がある。その中でも、3 つの原則についてピックアップして紹介しておく。
単一責任の原則 (SRP: Single Responsibility Principle)
モジュールを変更する理由は1つ であるべき
モジュールは1つのアクターに対して責務を負うべき
複数のアクターがいたら、そのコードが似通っていても コードは分割すべき
オープンクローズドの原則 (OCP: Open-Closed Principle)
ソフトウェアの構成要素は、拡張に対しては開き、修正に対しては閉じている必要がある
ソフトウェアの振る舞いは、既存のコードに手を加えずとも拡張できるべき
ちょっとした拡張のために大量の書き換えが必要になるようではダメ
上記を実現させるためには、変更を最小限にとどめるため、保護の階層 を意識する必要がある
保護というのは、変更からの保護、のこと
コンポーネント A が コンポーネント B の変更から保護されるべきなら、依存は B -> A の方向になるべき
どのコンポーネントを上位/下位に配置すべきかは、コンポーネントがいつどのような理由で変更されるかを考慮し決める
保護 (依存関係) の向きの制御に使えるテクニック
方向の制御 : DIP によって依存関係を逆転させる
情報隠蔽 : あるコンポーネントがあるコンポーネントの内部を知りすぎないようにする
依存関係逆転の原則 (DIP: Dependency Inversion Principle)
ソースコードを具象ではなく抽象に依存させる
変化しやすい具象がある場合 は、抽象に依存させるのが良い
変化しやすい具象より、安定した抽象を選ぶ
具象オブジェクトの生成が複雑になるなら、ファクトリーを用意する
DIP をやるとき、変更容易性に目がいきがちだが、どちらかというと効果が大きいのは、変更範囲を少なく抑えることができることかもしれない。DIP をしておくことで、周囲のコードがどれだけ変更されてもつられて変更する必要はなくなる。DI と DIP は異なる。DI していても抽象に依存していなければ、結局変更が必要になる
Clean Architecture 達人に学ぶソフトウェアの構造と設計 - 第 3 部 設計の原則 第 7 - 11 章
SOLID 原則の活用
ソフトウェアをソフトに保つ構造のためには、先に説明した SOLID 原則を活用できる。紹介した SOLID 原則は、どれも あるモジュールを変更したくなった時に、変更範囲を最小限にする のに役立つ。
例えば、単一責任の原則を活用すると、あるモジュールを変更した時に本来変更すべきでないコードまで意図せず変更してしまうことを防ぐことができる。
オープンクローズドの原則を活用すれば、あるモジュールを他のモジュールの変更から保護することができる。これは例えば、PDS を実現するのに活用できる。ドメインロジックを持ったモジュールとプレゼンテーションロジックを持ったモジュールが存在する場合は、ドメインロジックをプレゼンテーションロジックの変更から保護したいはずだ。
依存関係逆転の原則は、上記のオープンクローズドの原則をサポートできる。あるモジュールが変更しやすい具象に依存していると、その具象を書き換えるたびに、具象を参照するモジュールにも影響が出てしまうが、これを防ぐことができる。
このように、SOLID 原則はモジュールを適切に分割し、不必要な依存を取り除き、変更範囲を最小限にする。SOLID 原則はモジュール単位で適用できる原則だが、これは ソフトウェア境界の単位でも適用できる。
例えば、あるソフトウェア要素をその詳細から分離したい場合は、その間に境界線を引く、と話した。これはすなわち、あるソフトウェア要素を別の要素から保護したい、ということであり、オープンクローズドの原則や依存関係逆転の原則を活用すれば実現できる。
Clean Architecture
https://gyazo.com/6a18da31d0134b9f85d8640c014d677e
境界線をどのように引くか、の指針として参考になるアーキテクチャ方針として、Clean Architecture がある。Clean Architecture の唯一重要な原則は、ソースコードの依存関係が一方向に保たれていること である。層の数等はあまり重要ではない。中心が最も重要なビジネスロジックであり、通常、これを最初に考える。方針と詳細 で述べたことに従うと、円の中心から先に考え、一番外側の技術的な詳細については後回しにすることができる。
Clean Architecture における境界線の引き方は、以下のようになっている。
Domain: 企業のビジネスルール
Entity
Usecase: アプリケーションのビジネスルール。エンティティに入出力するデータの流れを調整し、ビジネスロジックを実行する
Usecase Interactor
Usecase Output Port
Usecase Input Port
Interface Adapter: Usecase, Domain/GUI 間でデータを変換する
Presenter
Controller
Framework & Driver: DB や Web フレームワーク等
Clean Architecture 達人に学ぶソフトウェアの構造と設計 - 第 22 章 クリーンアーキテクチャ
Reactive Programming
とか、最近のアーキテクチャについて紹介していきたい気持ちがある...