アーキテクチャ特性(-ility)とトレードオフ

『ソフトウェアアーキテクチャの基礎』 が中核に据える概念。システムの成功基準を定める「ドメインに依らない設計上の考慮事項」をアーキテクチャ特性と呼ぶ。非機能要件/品質特性とも呼ばれるが、著者らは QA 的に矮小化されるとして否定的。

特性の分類

  • 運用特性: 可用性、継続性、パフォーマンス、回復性、信頼性/安全性、堅牢性、スケーラビリティ。
  • 構造特性: 構成容易性、拡張性、インストール容易性、再利用性、ローカライゼーション(i18n)、メンテナンス容易性、可搬性、アップグレード容易性。
  • 横断的特性: アクセシビリティ(a11y)、認証/認可、プライバシー、セキュリティ、サポート容易性、ユーザビリティ。

トレードオフが本質

  • すべての特性をサポートする「汎用アーキテクチャ」はアンチパターン。パフォーマンスとセキュリティのように特性は互いに競合する。
  • 特性を入れるほど設計が複雑化するため最小化し、「少なくとも最悪ではない」アーキテクチャを iterative に狙う。
  • 特性に順序付けして全員合意を取るのは時間の無駄で、「選ぶだけ」にすべき。

発見と計測

  • 要件・ドメインの関心事から暗黙的特性を抽出する(ビジネス側の「スピード感」=アジリティ+テスタビリティ+デプロイ容易性、のように言い換える)。
  • 計測は客観的定義を必要とする。循環的複雑度(CC)などのコードメトリクスや First Contentful Paint などで統制する。アーキテクチャ適応度関数で継続的に検証。

関連: software-architecture-characteristics / software-architecture-characteristics / _moc-life-misc

アーキテクチャスタイルの比較

『ソフトウェアアーキテクチャの基礎』 が後半で列挙する代表的なアーキテクチャスタイルの俯瞰。スタイルは FE/BE のソースの構成方法とデータストアの相互作用を規定するもので、「構造」であって「アーキテクチャ」そのものではない。

モノリシック系

  • レイヤードアーキテクチャ: プレゼン/ビジネス/永続化/DB の水平層。技術駆動パッケージング。シンプルだが弾力性・スケーラビリティが低く、シンクホールアンチパターンに陥る。詳細は software-architecture-characteristics
  • パイプラインアーキテクチャ: producer→transformer→tester→consumer のパイプとフィルター。シェルのパイプや MapReduce が例。
  • マイクロカーネルアーキテクチャ: コアシステム+プラグイン。IDE・ブラウザ・税務申告ソフトなど。レジストリ(ZooKeeper, Consul)とコントラクトで拡張。

分散系

  • サービスベースアーキテクチャ: DB を共有する粗粒度サービス群。最も柔軟なスタイルの一つ。
  • イベント駆動アーキテクチャ: ブローカー型(高スケール)とメディエータ型(ワークフロー制御)。非同期(request-reply / fire-and-forget)、相関ID、データロス対策を扱う。domain-driven-design と接続。
  • スペースベースアーキテクチャ: タプルスペース由来。処理ユニット+仮想ミドルウェア+データポンプで中央DBのボトルネックを回避。チケット販売など急増負荷に強い。
  • SOA(オーケストレーション駆動): 再利用最優先だが結合が増える負のトレードオフ。
  • マイクロサービス: DDD の境界づけられたコンテキストでモデル化。再利用より複製、サーガパターンで分散トランザクションを補償。software-architecture-characteristics

選定

分散の8つの誤信(ネットワークは信頼できる/レイテンシ0…)を踏まえ、ドメインとトポロジーの同型性を見て選ぶ。すべては アーキテクチャ特性 のトレードオフに帰着する。

関連: _moc-life-misc

クリーン/レイヤードアーキテクチャ

層に分け依存方向を整理することで、ビジネスロジックをフレームワーク・DB・UIから独立させる設計。_moc-web-infra

基本原則

「関心の分離」と「依存性の整理」に帰着する。view にビジネスロジックを書かない。ユースケースは Interactor として実装し、コードのエントリポイントからロジックを分離する。クリーンアーキは厳密には「オニオン+SOLID」と捉えると分かりやすい。

層と責務

  • Controller/Presentation: validation、モデル⇔DTO変換。
  • Service: ビジネスロジック。トランザクション境界をここに設ける。
  • Repository: 永続化操作の抽象。実装は infra 層(RepositoryImpl)に置き、domain は infra に依存しない。
  • domain に infra(tx, 一貫性チェック)が混入するのを100%は防げない。責務分離が目的。

サービス間呼び出しの是非

  • ユースケースは基本再利用しない。Service から別 Service を呼ぶのは原則禁止。
  • 共通化したい処理は SharedService に切り出す(トランザクション境界にならないことが多い)。
  • ユースケースが肥大化したらアクターを小さく分ける(支払い部分だけ Payer にする等)。そもそも巨大なユースケースは設計が間違っている可能性。

トランザクションの横断

N層アーキテクチャでトランザクションをクリーンに扱う解の一つが Unit of Work パターン。複数のRepository操作を1つの作業単位としてコミット/ロールバックする。DI とインターフェースで実現する。

設計判断

技術者は特定技術に熱中しやすいので、トレードオフ分析(分析的視点で良し悪しを判断)が重要。弾力的スケーリング(ユーザー数に応じたスケール)など非機能要件も設計に含める。

関連

マイクロサービスアーキテクチャ

サービスを業務ドメインごとに分割し、独立してデプロイ・スケールさせる構成。_moc-web-infra

サービス間通信

  • grpc によるRPC、redis Streams 等のメッセージング。
  • Message Queuing(MQ): 非同期にタスクを渡す。PostgreSQL を MQ として使う(sqlxmq, job queue 管理テーブル)実装もある(postgresql)。
  • リトライは exponential backoff(失敗のたび待ち時間を倍に)。

分散トランザクション: Saga Pattern

複数サービスにまたがる処理を、各サービスのローカルtxと補償処理の連鎖で実現する。例: ウォレットサービス(残高)→チケットサービス(発行)をイベントキュー(Redis Stream)+ gRPC で繋ぐ。強整合性を諦め結果整合性に倒す。

段階的移行: ストラングラーパターン

ストラングラーファサード(API Gateway で振り分け)を作り、レガシーを徐々にモダンへ置き換える。モノリスからの移行の定石(クックパッドの Rails 100万行のマイクロサービス化など)。

インフラ用語

  • LB(ロードバランサ): L4 LB はトランスポート層(IP/ポート)で振り分け軽量、L7 LB はアプリ層(ヘッダ/Cookie)を見て柔軟に振り分け。
  • スティッキーセッション: 同一ユーザーを同一サーバへ。スケール時に分散できないので、セッションを Redis に外出ししてステートレス化する。
  • サービスメッシュ(Istio)、ネットワークプロキシ(Envoy = L4/L7 フィルタ・ルーティング・LB)。
  • CNCF: Prometheus(監視)/Grafana(可視化)/kubernetes/Helm/Terraform(IaC)。

設計上の注意

  • 業務分担を横割りから縦割り(ドメイン単位)に。ドメイン理解が最重要。
  • 偶発的プログラミングを戒める。ビジネス定数はデータと処理の分離・変更容易性の観点から設定ファイルへ。

関連