emahiro/b.log

Drastically Repeat Yourself !!!!

読書 Note -『Righting Software』- 第一部 -「構造」と「組み立て」

Overview

Righting Software を少しずつ読み進めていく中で第一部の 3~5章に書かれているソフトウェアの設計の「構造」と「組み立て」の内容が、今まで目にしてきた設計系の書籍やブログを読んできた中で一番腹落ちした内容 (*) だったので書籍に記載された構造についての自分なりのメモや考えたこと、及び Go で具体的に実装したサンプルをログとしてまとめておくためのエントリ。

*個人的には名著『Clean Architecture』よりもわかりやすい内容だと思った。

今回の学習内容は以下にまとめてある。

github.com

読書 Note

まず Righting Software に構造と組み立ての章に関して気になったところを簡単にまとめる。

ユースケースと要件

  • 要件は必要な機能ではなく、必要な振る舞いを明らかにするものではなければならない。
  • システムが何をすべきかではなく、システムがどのように動作しなければならないかを規定する。

必要な振る舞い

  • 必要な振る舞い(= システムがなんらかの仕事を達成してビジネスに付加価値を与えるためにどうしなければならないのか?) を表現したものが ユースケース
  • ユースケースはシステム内のアクティビティの特定のシーケンスになる。
  • ユースケースはエンドユーザーとシステムのインタラクションやシステムと他のシステムとのインタラクション、バックエンドの処理を記述できる。

表現方法

  • ユースケースは文章でも図でも記述できる。
  • 文章によるユースケースは簡単に作れ、そのことは大きなメリットだが、表現方法として図よりも劣っている。
    • 人間は文章をわざわざ読もうとしない笑

ユースケース図とアクティビティ図

ユースケース

f:id:ema_hiro:20210811162611p:plain

このレベルのユースケース図は簡単に記述できるし、図じゃなくても文章で簡単に記述できる。ただ、最初から図を書いた方がいい。

アクティビティ図

こちらを書籍で推奨しているのはこちら。
アクティビティ図は振る舞いの時間的な側面を捉えることができる(待ちや並列実行など)点もユースケース図よりも表現の幅がある。

f:id:ema_hiro:20210811174457p:plain

階層化されたアプローチ

  • ザ・メソッドでは階層構造を重用している。
  • カプセル化も階層構造である。
  • 個々の階層はそれぞれの変動性と下位階層の変動性をカプセル化して上位階層から見えないようにしている。

サービスの活用

階層を超える方法として望ましいのは サービスの呼び出し である。
サービスを使えば以下のようなメリットが得られる。

  • スケーラビリティ
    • サービスインスタンスは呼び出しごとに生成できる。
    • 進行中の呼び出し数と同じ数だけサービスインスタンスがあればよい。クライアントの数が多くても、比例してバックエンドリソースに負荷をかけずに済む。
  • セキュリティ
    • サービス指向プラットフォームはどれもセキュリティを重視している。
    • サービス間呼び出しにも認証認可が必要になり、なんらかの ID 伝播メカニズムを使うことになる。
  • スループットと可用性
    • サービスはキューを通じた呼び出しを受けつけることができることで、負荷の上限をオーバーする分の処理はそのままキューに貯めておく、という方法で膨大な数のメッセージを処理することができる。
  • 応答性
    • サービスであれば呼び出しをバッファリングして、システムが限界を超えることを避けることができる。
  • 信頼性
    • クライアントとサービス間では到達の保証、ネットワーク接続障害の処理、呼び出し順序の指定などに信頼性の高いメッセージングプロトコルを使うことができる。
  • 整合性
    • 結果整合性を保証する協調的なビジネストランザクションを使えば1つの作業に複数のサービスを参加させることができる。
    • 呼び出しチェーンの過程でエラーが発生したら、作業全体をなかったことにする。
  • 同期
    • クライアントが複数の並行スレッドを使っていたとしても、サービスの呼び出しは自動的に同期される。

階層の種類

本書には以下の階層(レイヤー)があると書かれている。

  • クライアント層
    • プレゼンテーション層とも呼ばれる。
    • エンドユーザーのユーザーアプリケーションを指すことが多い。
  • ビジネスロジック
    • ビジネスロジックの変動性をカプセル化する。
    • このレイヤーがシステムに必要とされる振る舞い(=ユースケース)を実装している。
    • ユースケースは顧客と時間の両方の変動性を孕んでいて、ユースケースの変更はユースケースを構成するためのシーケンスの変化、またま、シーケンス内の各アクティビティの変化の二種類しかない(*1)
    • 実装としては Manager と Engine がこのレイヤーでそれぞれカプセル化する。
      • Manager はシーケンスの変動性をカプセル化する。
        • ex. ビジネス都合で並行処理だったプロセスを直列に変更する etc...
      • Engine はアクティビティの変動性をカプセル化する。
        • ex. あるシーケンスの中に存在する通知アクティビティにおいてユーザーに通知していた内容を社内向けにも通知する etc...
      • Engine は Manager よりも関心の範囲が狭く、ビジネスルールやアクティビティそのものをカプセル化している。

*1. ユースケースの変動性は以下のように並行処理で実現されたユースケースが、ビジネスの変化により直列の処理が必要になった、というようなものがあげられる。

f:id:ema_hiro:20210811181857p:plain

  • リソースアクセス層
    • 文字通りリソースアクセスにまつわる変動性をカプセル化する。
      • なぜならリソースが永続的なストレージか一時的なインメモリストレージ化によってアクセス方法は変動するから。
    • ミドルウェアを類推させるような名前をインターフェースの名前に使わない。
      • インターフェースに select() や insert() があると裏には RDBMS があることを類推させてしまい、そんなにないと思うがリソースの変更に弱い。なぜなら、リソースを変更するとリソースアクセス層も変更しないといけなくなるから。
      • 同様に Open() や Close()、Write() といったインターフェースは裏側にファイルシステムがあることを類推させてしまい同上。
  • リソース層
    • 実際にDBやファイルシステム、キャッシュにアクセスするレイヤー
    • memo
      • ミドルウェアのクライアント部分に当たるので ifra 層などと呼称されるケースもあるレイヤー。
  • ユーティリティ層
    • ロギング、認証認可、pubsub など全てのシステムが横断で利用するものをまとめておくレイヤー。

その他

第3章前半で構造についての説明やガイドライン書かれていて、後半では拡張性やマイクロサービスのことも触れられている。
第4章では第3章で説明された階層や各コンポーネントの組み合わせ方(組み立て)について記載されている。 後半に記載してる Go のサンプルではその組み立て方もある程度参考にしている。 第5章では 3, 4 章で書かれている内容をベースにしたケーススタディになっている。

今回は構造についてまとめておきたかったので第3章の前半をベースにまとめているが、設計については第一部を通読するのは復讐も兼ねて勉強になると思う。

階層についての個人としての解釈・考察

各階層について

  • クライアント層
    • ユーザー及び、システムを使う側の最前面に露出するところなのでもっとも変動性が大きい。
  • ビジネスロジック
    • ここが一番腹落ちした。DDD とかで語られるレイヤーわけでよく見かけるユースケース層というものの本当の意味がわかった気がする。
    • マネージャーはユースケースを表現している。
      • マネージャーはユースケースを管理する。
      • Engine はアクティビティ図における各アクティビティノードを表現している。
      • マネージャーは Engine およびリソースアクセスレイヤーをインスタンス化し、それらをオーケストレーションする役割を担う。
      • ユースケースの変更があっても本来的にはマネージャーのみの変更で済むはずで変更が局所的になり、システムの振る舞いの変更に強くなる。
      • マネージャにはユニットテストが欲しくなる。なぜならビジネスロジックを実装として表現してるものだから。
  • リソース層
    • ミドルウェアのクライアント部分に当たるので ifra 層などと呼称されるケースもあるレイヤー。infra レイヤーと書かれると馴染みのあるレイヤーだなと思う。

ユースケースとアクティビティ図の考え方について

各アクティビティがドメインの受け持つロジックに関心事があり、マネージャーはそのドメインロジックを協調させてユースケース(=ある要件に対してのソフトウェアの振る舞い)を実現するものと捉えると、階層構造をとるアプリケーションの捉え方が少し変わった。
概要で参照してる PullRequest にて実装した設計は Righting Software で言及されている階層と組み合わせを、 ユーザー登録のユースケースをベースにして 以下のようなシーケンスをイメージして実装している。

これを図に起こすと以下のようなイメージになる。

f:id:ema_hiro:20210811191025p:plain

まとめ

階層構造を取るアーキテクチャについてずっとわかったようなわからなかったようなところを行ったり来たりしていたが、『Righting Software』に書かれている構造と組み分けの章を読んでみて、アクティビティ図とセットで考えることでようやく雰囲気で理解していたカプセル化の意味を少し実感できた気がする。
後半のプロジェクトデザインの章については、時間があればまた別のエントリでまとめたいと思う。