【メモ】アーキテクチャについてとSOLIDの原則

最近、「[クリーンアーキテクチャ](https://www.amazon.co.jp/dp/B07FSBHS2V)」という本を読みました。プログラミング関連の書籍としてはかなり有名な本で、様々な側面から記事がたくさん上がっております。

- [世界一わかりやすいClean Architecture](https://www.nuits.jp/entry/easiest-clean-architecture-2019-09)
- [実装クリーンアーキテクチャ](https://qiita.com/nrslib/items/a5f902c4defc83bd46b8)
- [Clean ArchitectureをNode.js+Typescriptで実装してみる](https://blog.spacemarket.com/code/clean-architecture-node/)

自身もこういった記事を書こうかとは思うのですが、何しろ設計関連の書籍は初めて読んだので、手前の段階できちんと理解できていない単語にたくさん直面しました。
今回は「アーキテクチャとは何か」と「SOLIDの原則」について書いていきたいと思います。

## アーキテクチャとは

自身は設計段階からプロダクトの開発に携わったことはないですが、日々コードを書いている時に最も考えていることは「わかりやすさ」です。これは「[リーダブルコード](https://www.oreilly.co.jp/books/9784873115658/)」を読んだことが影響しています。
アーキテクチャに関してもきっと似たような概念が適用できるのだろうと常々考えていたのですが、この予想は正しかったです。クリーンアーキテクチャでは「アーキテクチャ」をこのように説明しております。

> ソフトウェアアーキテクチャの目的は、求められるシステムを構築・保守するために必要な人材を最小限に抑えることである。

> アーキテクチャの主な目的は、システムのライフサイクルをサポートすることである。

> ソフトウェアアーキテクチャの目的は、システムを単一のアクションで簡単にデプロイできるようにすることである。

このあたりの引用から察するに、詳細レベルのコードに限らず、アーキテクチャにおいても「わかりやすさ」を重要視しても良さそうです。

## SOLIDの原則とは

それぞれの頭文字を取ってくっつけた、5つのソフトウェア設計原則をまとめた名称のことです。またこれらが作られた目的は下記のような性質を持つソフトウェア構造を作ることです。

- 変更に強い
- 理解しやすい
- 多くのソフトウェアシステムで利用できること

最後の「多くのソフトウェアシステムで利用できること」に関してはピンときていませんが、上の2つを目的に設計を行うだけでも十分だと考えています。
以降でそれぞれの原則について説明していきます。

### 単一責任の原則(SRP)

> モジュールはたったひとつのアクターに対して責務を追うべきである

ここにおけるモジュールはソースコードのことと理解して差し支えないです。また「アクター」に関しては「単一のユーザー属性」と言いかえることができます。
例えば何らかのメディアを想像すると「記事を見る人(ユーザー)」と「記事を書く人(管理者)」がいますね。それぞれが「アクター」となります。

クリーンアーキテクチャでは給与システムの例が挙げられていました(図まで書くと超大作になるのでやめておく)。
所定労働時間の算出方法に手を加えたら、別の目的で同じメソッドを利用していた箇所に意図しない挙動変更が混じってしまい、損失を食らうといった事例です。
この原則の主目的は**意図しない重複**を防ぐことにあります。用途は同じでも利用するアクターが違う場合は重複させないようにしましょう。

### オープン・クローズドの原則(OCP)

> ソフトウェアの構成要素は拡張に対しては開いていて、修正に対して閉じていなければならない

とある機能を拡張しようとした際に、大量の書き換えを要するのであれば、アーキテクチャとしてそれは失敗しているということになります。

自身はそのような悪いケースに遭遇したことはないのですが、例えば書かれていた例ですと、Webページで表示していた情報を白黒のPDFで書き出したいというケースですね。
Railsで構築されたシステムにおいて、表示に必要な情報の整形をControllerのレイヤーで行っていた場合、PDFに書き出すために同様の処理をControllerに記述する必要があります。
こういった時に整形処理を別クラスに切り出しておけば、出力処理を切り替えるだけで済むようになります。
機能の拡張がしやすいようにコンポーネントを分割しましょう。

### リスコフの置換原則(LSP)

Wikipediaから引用するとこんな説明が書かれております。

> 型システムなどにおける型の派生に関する議論において、派生元の型（基底型）と派生先の型（派生型）の間に成り立っていなければならない規則性

要するに規定型のオブジェクト(クラスでも可)を用いた実装において、その部分を派生型に置き換えてもエラーなく動作させなくてはならないということですね。

TypeScriptやGolangなどの静的型付け言語を用いて開発しているのならば、さほど意識せずともこの原則は満たせると思います。
しかし[こちらの記事](https://qiita.com/yuki153/items/142d0d7a556cab787fad#%E3%83%AA%E3%82%B9%E3%82%B3%E3%83%95%E3%81%AE%E7%BD%AE%E6%8F%9B%E5%8E%9F%E5%89%87%E3%81%AB%E9%81%95%E5%8F%8D%E3%81%97%E3%81%A6%E3%81%AF%E3%81%84%E3%81%91%E3%81%AA%E3%81%84%E7%90%86%E7%94%B1)で記載されている例や、本書に書かれていた「様々なタクシー会社をまたいで配車を行えるサービスにおいて、1社だけがWebAPIのパスが異なってしまった」といったような事象もあります。
モジュール間・コンポーネント間において置換可能性(依存先のクラス等を派生したものと入れ替えても問題なく動くこと)は守りましょう。

### インターフェイス分離の原則(ISP)

**不要なものには依存しない**ということですね。
本書では、3つのメソッドが定義されているクラスに対してそれぞれのメソッドを利用する3つの別クラスが依存している例がありました。
この場合ですと、3つのインターフェイスを作って依存先を分離するようにしていました。
この原則に違反した場合、[こちらの例](https://note.com/erukiti/n/n3daa55541bc8#UuB9J)のようにアクターの違うメソッドと共通化したがゆえに複雑な処理となってしまう場合があります。
不要なものへの依存は避けましょう。

### 依存関係逆転の原則(DIP)

> ソースコードの依存関係が（具象ではなく）抽象だけを参照しているもの。それが、最も柔軟なシステムである。これが「依存関係逆転の原則（DIP）」の伝えようとしていることである。

まさに引用の通りで、具体的に言い換えるなら「具象クラスではなくインターフェイス(抽象クラス)に依存せよ」ということでしょう。
本書では実例としてAbstract Factoryパターンを紹介していました。自身は読んだ時理解できなかったのですが、[具体的な実装](https://blog.spacemarket.com/code/clean-architecture-node/)を見ると理解できました。
たしかにこれなら可読性が高いまま変更容易性を保つことができますね。

## 終わりに

アーキテクチャ関連の用語を覚えるメリットとしては下記のようなものでしょうか。

- 自身が設計する際に知見を活かせる
- レビューの際に引用することで相手に納得感が得られる
- 共通言語として存在するので説明が省略できる

クリーンアーキテクチャを読んで自身の仕事に活かせれられると嬉しいです。

次回は実際にクリーンアーキテクチャでかんたんなサンプルコードを作っていこうかと思います。