目次
アーキテクチャ
目的
- システムのライフサイクルをサポートすること
- システムを容易に理解・開発・保守・デプロイできるようにすること
- システムのライフタイムコストを最小限に抑え、プログラマの生産性を最大にすること
優れたアーキテクチャとは
- 方針と詳細を区別して、方針が詳細を把握することなく、決して依存することがないように、両者を切り離す
- 詳細の決定をできるだけ延期・保留できるように方針をデザインする
なぜ詳細の決定を後回しにするのか
- 適切に作るための情報が数多く手に入るから
- 複数の実験をする選択肢も与えられる
開発初期にやらなくていいこと
-
詳細を決めること
- データベースシステムを選択すること
- webサーバーを選択すること
- RESTを採用すること
- DIフレームワークを導入すること
独立性
ユースケース
- アーキテクチャレベルでシステムの意図がわかるように、振る舞いを明らかにする
- 優れたアーキテクチャはシステムのユースケースがシステムの構造のなかにはっきりと見える
運用
- 優れたアーキテクトは運用方法の決定は選択肢として残しておく
- コンポーネントを適切に分割し、コンポーネントの通信方法として特定の技術を想定していないアーキテクチャは、運用ニーズの変化に合わせて、スレッド、プロセス、サービスなどへと比較的簡単に移行することができる。
開発
- コンウェイの法則:システム設計する組織は、組織のコミュニケーション構造をコピーした構造の設計を生み出す。
- 開発中はお互いが干渉いないように単発で開発可能なコンポーネントにシステムを適切に分割し、それぞれのチームに割り当てる
デプロイ
- 即時デプロイできる環境を目指す
- システムをコンポーネントに適切に分離・分割することで実現できる
レイヤーの切り離し
- システムは切り離された水平レイヤー(UI, アプリケーション特有ビジネスルール, アプリケーションに非依存なビジネスルール, データベース等)
ユースケースの切り離し
- ユースケースはシステムの水平レイヤーを薄く垂直にスライスしたもの
- ビジネスルールについてもデータベースについても分割の高さを揃えておく
切り離し方式
- コンポーネントの切り離しで、運用上の適切な方式は違う場合がある
方式の種類
-
ソースレベル
- モノリシック構造
-
デプロイレベル
- デプロイする単位を切り離す
-
サービスレベル
- 分割してコンポーネントは異なるサーバーに配置して、何らかの手段で通信する必要がある
- このようなコンポーネントをサービスまたはマイクロサービスと呼んでいる
結論
最適な方式は開発初期段階では判断が難しいので、移行しやすいようにコンポーネントを切り分ける
コードの重複
- コードの重複を目にした時、すぐに共通化したいと思うかもしれない
-
重複には2種類ある
- 本物の重複
-
偶然の重複
- 時間が経てば別物へと進化するもの
- 反射的に重複を嫌うのではなく、本物かを見極める
バウンダリー:境界線をひく
バウンダリーとは
- ソフトウェアの要素を分離し、お互いのことがわからないように制限する
どこに境界線を引くか
- GUIとデータベース
- GUIとビジネスルール
-
ビジネスルールとデータベース
- ビジネスルールはデータを取得・保存する機能が存在することだけ知っていればいい
プラグインアーキテクチャ
- コアのビジネスルールに他のコンポーネントはプラグインとして依存するように設計する
- 下位レベルのコンポーネントは上位レベルのコンポーネントに「プラグイン」されるべきである。
ビジネスルール
- ビジネスマネーを生み出したり節約したりするルールや手続き
最重要ビジネスルール
-
システムが自動化されていてもいなくても変わらないビジネスルール
- 銀行がローンにN%の利子をつけているというお金を産むためのビジネスルール
最重要ビジネスデータ
-
最重要ビジネスルールに必要なデータ
- ローンであれば、貸付金残高、金利、支払いスケジュール等
叫ぶアーキテクチャ
-
アーキテクチャはフレームワークに依存ではなくユースケース中心に設計されるべきである
- ヘルスケアシステムを構築しているならアーキテクチャのソースリポジトリを見た時に「ヘルスケアのサービスである」と叫んでいなければいけない
- フレームワークを使うことなく、全てのユースケースのユニットテストを実行できるはず
- テストをするためにサーバーの起動やデータベースの接続は必要ない
クリーンアーキテクチャ
特性
-
フレームワーク非依存
- アーキテクチャが機能満載のソフトウェアライブラリに依存していない。これによりフレームワークの制約で縛るのではなく、フレームワークをツールとして使用できる
-
テスト可能
- ビジネスルールはUI、DB、サーバー、その他の外部要因なしにテストできる
-
UI非依存
- UIはシステムの他の部分を変更することなく簡単に変更できる
-
データベース非依存
- OracleやSQL ServerをMongo、BigTable、CouchDBなどに置き換え可能、ビジネスルールはDBに束縛されてない
-
外部エージェント非依存
- ビジネスルールは外界のインターフェースについて何も知らない
依存性のルール
- ソースコードの依存性は内側(上位レベルの方針)だけに向かっていなければいけない
- 外側の変更が内側に影響を及ぼさない
エンティティ
- 最重要ビジネスデータを操作する最重要ビジネスデータをいくつか含んだオブジェクト
- ビジネスにとって不可欠なもの、つまりビジネスそのもの
- 何にも依存しない
ユースケース
- 自動化されたシステムを使用する方法を記述したもの
- エンティティとは違い、ユースケースはアプリケーション固有のビジネスルールを記述
- エンティティの最重要ビジネスルールをいつ・どのように呼び出すかを規定したルールが含まれている
- ユーザーインターフェースには記述せず入力されるデータを略式で規定する
- エンティティに依存している
インターフェイスアダプター
- ユースケースから外部へのデータを変換を担う
- 外部からユースケースへのデータを変換を担う
境界線を超えるデータ
- 境界線を超えるデータは単純なデータ構造であることが大切
プレゼンターとHumble Object
Humble Objectとは
-
下記に2つのモジュールまたはクラスに分割すること
-
テストしにくい振る舞い
- こちらのコードはテストがしにくい分できるだけシンプルにする
- テストしやすい振る舞い
-
テストしにくい振る舞い
-
データベース、外部サービス、UIなど境界線の付近にはhumble objectを使用する
- テスト容易性が大幅に向上する
GUIのユニットテストは画面に適切な要素が表示されているかを確認するテストを書くのが難しい。そういった時にHumble Objectパターンを使用する
- 2種類の振る舞いをPresenterとViewに分ける
参考:https://shtnkgm.com/blog/2020-05-17-humble-object-pattern.html
部分的な境界
アーキテクチャの境界はコストが高い
境界を実装するコストと無視するコストを比較検討し、その結果を何度も評価する。無視するコストよりも実装するコストが低くなる変曲点で、境界を実装することがゴールである
部分的に境界を作ってコストを抑えたい時の方法
-
最後のステップを省略する
- 独立してコンパイルやデプロイ可能なコンポーネントを準備した後で、それらを同じコンポーネントにまとめる
-
片方だけの境界
- 両側のインターで椅子を使用せずに、片方だけにインターフェイスを使用する
- 具体的にはStrategyパターンを用いる
-
Facade
- 境界をFacadeクラスで定義しておいて、クライアントからアクセスできないクラスにサービス呼び出しを行う
テスト境界
- テストはシステムの一部
- 非常に詳細で具体的であり、テストするコードに対して常に依存している
- アーキテクチャの円の最も外側であると言える
- 独立デプロイできなければならない
-
変化しやすいものに依存しない
- 例:GUI
- 変化しやすいものに依存すると変更したときに多くのテストが壊れる
クリーン組み込みアーキテクチャ
-
ソフトウェアは消耗しないが、ファームウェアやハードウェアは時代遅れになる。その結果、ソフトウェアの変更が必要になる。
- ファームウェアは「何に依存しているのか、ハードウェアの進化に合わせてどれだけ変化しにくいか」で決まる