DDDからみた連携パターン(SOA,MSA)

去年の夏前くらいにDDDまわりの勉強していた。でこのメモを書いていたので少し直して晒してみる。 前回の続編。

ソフトウェアアーキテクチャや設計論はたくさんあってややこしい。なので自分なりに整理してみる。

DDDの戦略的設計で出てくるコンテキストマップで現れるコンテキスト間の関係の分類をいくつか行う。 またコンテキスト間の接続を何でするかについて言及されていたものを中心に列挙する。

最後に 連携パターンとしてのアーキテクチャ のなかでコンテキストの独立と連携を同時に扱うマイクロサービスアーキテクチャ(MSA)とサービスオリエントアーキテクチャ(SOA)について説明する。

サブコンテキストの関係

DDD本では戦略的モデリングとしてサブコンテキスト同士の関係についてパターンを見出している。

これらの特徴としてモジュールのインタフェースや依存関係以外に開発や運用体制など組織も扱っていた。 そのため1つの用語で無自覚に2つの以上のことを一緒に考慮していて理解しづらくなっていると感じた。何より分類になってなくて使いづらい。

とりあえず紹介されているものを要約しつつ並べる。

IDDD本では更にパートナーシップを加えているので下の表には追加した。

コンテキスト間パターン 意味
パートナーシップ コンテキスト同士が相互依存している。チームとして協力が必要(IDDD本のみ)
共有カーネル モデルやコンポーネントを共有している。チームとして協力が必要
顧客/供給者の開発 U/Dの依存関係が存在する。UにとってDは重要
順応者 U/Dの依存関係が存在する。UにとってDが重要ではない
腐敗防止層(ACL) コンテキスト間でデータ変換で(DDD本では特に防御的なもの)を指す
公開ホストサービス(OHS) プロトコルを選定・公開して利用者が使用できるようにする
公表された言語(PL) 標準化されたデータ表現形式を利用する
別々の道 要件を調整して連携をなくす
巨大な泥団子 既に複数のドメインにまたがる巨大なコンテキストなので諦める

前述した通り、使い勝手が悪い気がしたので気になる部分を挙げておく。

開発・運用体制にもスポットを当てた用語が含まれるためリファクタやチームの再編成に利用されるものが多いが、一部はインタフェースといったシステムの側面だけを扱うものも含まれており、それらの違いに触れず一緒くたに扱っている。 非常にわかりにくいので、特にシステム構成を直接表している項目を別に切り出すべきだと思われた。 また公開ホストサービス(OHS)と公表された言語(PL)の違いが曖昧でわかりにくい。

これらがあってか、IDDD本では公開ホストサービス(OHS)・公表された言語(PL)・腐敗防止層(ACL)を特別に扱っている。そして曖昧だったOHSとPLはOHS/PLと一括で扱う事が多い。 この3つは他のパターンと異なり連携方法を意味する用語になっているためだと思われる。

IDDD本では、3つのパターンを特別は重要視されていてコンテキストマップにアノテーションを加える事を推奨している。

注記付きコンテキストマップ 注記付きコンテキストマップ

まず連携するサブコンテキストの間に上流(U)/下流(D)という依存関係を見つけコンテキストマップに書き込む。 そして上流側にOHS/PLを下流側にACLを明記する。これによってプロトコロル公開の責務やデータ変換が必要となる箇所を明示させる。

ここでは戦略的モデリングの進め方は触れない。

変換層の実装パターン

DDD本/IDDD本ではモデルの変換も小さいサブコンテキストと捉えていた。

複数のサブコンテキストが連携するにはモデルの変換が必要となる。 その変換は頻発するため実装パターンが紹介されている。

この図を具体的に書き直す。

このようにリポジトリやサービスを経由して外部コンテキストを利用する。 そのなかでトランスレーターを利用して自コンテキストに向けた変更を行う。 トランスレーターが両コンテキストについて知っている状態になる。

擬似コードを書くと下みたいな感じ。いたって普通。

1
2
3
4
5
6
7

class UserRepository(user: String, pass: String) {
  val client = OrangeServiceClient(user, pass)
  fun getUser(id: String): User {
    return Convert.fromOrangeUser(client.getUser(id))
  }
}

このようなモデル変換を通してサブコンテキストが連携する。 このパターンではトランスレーターもドメインモデルを扱っていて通信プロトコルなど結合方法を扱っていない。

サブコンテキスト間の技術的側面

コンテキスト間の連携は技術的には色々とありDDDでも言及がある。ざっと並べる。

  • ライブラリ(コード共有)
  • テーブル共有(データ共有)
  • REST API
  • RPC
  • Pub/Sub
  • MQ

最後の4つはコンテキスト境界を強制する。 ただしライブラリやテーブルではなく一旦インフラやミドルウェアを経由するのでインフラ依存が発生する。 そのためドメイン知識にインフラ依存を混在させるか分離するか考えることになる。

自分の考えは前に感想で書いたけど、本としては分離させるのが 基盤となるアーキテクチャ の役割と機能になっている。 IDDD本ではヘキサゴナルアーキテクチャを利用して分離することを提案している。 基盤となるアーキテクチャ として最近はクリーンアーキテクチャが流行っているらしい。 ゆるく使えばいい。変更が伝搬する依存関係を使わない要するのが大事。

コンポーネントをサービスとして提供する最近の動向

前述した切り離す技術について少しメモしておく。

REST APIだとOpenAPI, RPCだとgRPCが最近は多いように思う。

gRPCHTTP/2を前提にしてストリーミングが提供されているの面白いなぁと思ってる。 特にgRPC-Webによってサーバー,アプリだけの技術からWebに含まれるのも楽しみだ。

新しい特にB2Cなサービスは、インフラ調達や開発速度を背景にはじめWebで構築されて、UXやセキュリティなどからアプリやOSやハードに移行していくんだろうなぁって思う。gRPCで作っておくとアプリへの移行までならしやすい形で開発できそうだと思った。

OpenAPIは元々のSwaggerとの間で分離が起きたので心配。 また、JSONに対する制約という感じで柔軟で型システムと相性が悪そうに思える。

最近はHTTP上でGraphQLを使ってクエリを提供するサービスを構築するのも増えてきているらしい。 ちょっと前にGraphQL FoundationがLinux Foundation傘下に作られたみたいなので安心して触っていきたい。

MQはプロトコルも実装もサービスも大量にある。 Pub/SubとMQは両方ともメッセージパッシングなインタフェースなので似てると誤解されがちなので補足しておく。

Pub/SubはSubscriberが不特定多数でありデータの上流が 知られた サービスになる。 一方でMQではキューを経由して特定のサービスが1回ほど実行されることが期待されている(キューからメッセージをreceiveするプロセスが実際には多数であったり、キューの一貫性としてが At Least Once/At Most Once などの場合でも)。下流が 知られた サービスになる。

MSAとSOA

何度も書いているけどサブコンテキストを独立させると全体として柔軟になり連携機能の提供が簡単になる。 実際にIDDD本ではヘキサゴナルアーキテクチャを他のパターンとしてのアーキテクチャと関係付けて説明している。

このサブコンテキストの独立と似た考え方を主張しているものにサービスオリエントアーキテクチャ(SOA)やマイクロサービス(アーキテクチャ)(MSA)などがある。

そこでSOAやMSAの指すアーキテクチャがどのようなものか考える。

SOAやMSAは共に、アプリケーションを役割を明確に持った独立したサービスを組み合わせて作ろうとする。 そしてアプリケーションの設計パターンや方針だけではなく組織構造なども含めた指針を提示してる。

似ていることもありこだわりの強い人たちがかつて色々と議論を起こしていた。

マイクロサービスSOAにすぎないと指摘する人もいるらしい。 IDDD本やIBMの記事では、SOAについて論点や合意が内部でも取れずに噛み合わっていない事も多いと書かれている。 MSAを言い出したMartin Fowlerの記事では、SOAの方針は良いが具体的な判断基準などが提示されておらず実践的ではないと指摘している。 マイクロサービスアーキテクチャ本ではマイクロサービスはSOAの一つの実践形態として定義していた。

似てるし設計する時の考え方というだけなので、MSAとSOAの争いを掘り返すのは不毛だと思う。 とりあえず用語の整理がしたいため、具体性が高く新しい資料も多いMSAについて最初に整理する。 そのあとMSAな人がSOAを曖昧だと指摘している点を並べたりする。 MSAとSOAの違いが重要ではない。あえて指摘している人が現れる程度には、それが重要な設計観点になってると考えられるため。

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

マイクロサービス(アーキテクチャ)(MSA)では小さく自立したシステムの組み合わせとしてアプリケーションを構成する。

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

そして、技術異方性, 回復性, スケーリング, デプロイの容易性, 組織面の一致, 合成可能性, 交換可能にするための最適化といったメリットを得ようとする。 分割をすると新たに生まれる問題もあるため考えることは増える。 そのため単にサービスとして分割すれば良いわけではない。 分割によって生まれる問題とのコスパや分割する正当性を見極める必要がある。

たとえばコンポーネントが増えることで監視項目が増える。 そのため従来の方法では障害検知するのが困難になってしまう。 また障害対応も手間がかかる。 さらに大きな問題として統合された全体の挙動を把握しづらくなる。 ここではサービス分割について一般的に言われる指針とメリットについて説明する。

サービス分割の指針

マイクロサービスでは 自立したシステム の責任範囲については2つの基準がある。

1つめは 単一責任原則 を使いサービス境界をビジネス境界に一致させるというもの。 これはDDDではサブコンテキストをサービス候補とすれば良いと考えられる。 DDDでモジュールと呼ばれるサブコンテキストはライブラリにした方が良い場合が多いだろうしサブコンテキストをなんでもサービス化するわけではない。 この辺はサブコンテキストの内容だけでなくチームや会社の大きさなど運用体制からも判断が変わりえるので難しい。 (判断を見余って、自分達に合わないほど細かくするとマイクロサービス疲れになってしまう。)

2つめは サービスの大きさは担当チームより小さくする 。 チームの持つシステムによってチームの責任が限定されがちだ。 これを逆手にとり、ビジネス境界にチームを合わせるように組織を組み立てなおす。 これによりサービスの発展を邪魔しないようにする。 その判断基準として、サービスの規模を1チームで管理できる大きさに留めるという基準を設けている。

メリット

ここからはメリットについて説明する。

技術異方性

サービスごとに異なる技術を採用することができる、小さい単位で新技術を採用できるので検証後早めにできる。 またサービスが複数あるためリスクが小さい場所や効果を判断しやすいサービスで検証できる。

回復性

サービスが分割されているため統合方法によって障害範囲を限定して機能低下や縮退運用を可能にする。

スケーリング

サービス単位でスケーリングを行える。

デプロイの容易性

小さい単位でデプロイできるので頻度を上げられる。問題が生じた際に特定とロールバックを簡単にできる。

組織面の一致

1つのコードベースで作業する人を最小化しチームサイズや生産性を最適化させられる。

合成可能性

機能を再利用する機会を増やせる。そのためアプリケーション全体の再構築や状況変化に早く適応できる。

交換可能にするための最適化

レガシーシステムの置き換えや削除に対する心理的な抵抗や実際の書き換えコストを小さくすることができる。

サービスオリエンテーション(SOA)

SOAもほとんど同じだと思っている。 これらの議論から自分達の組織やサービスをよくする判断基準や考え方を作るのが大事だと思う。

なのでネットで拾った批判点を2,3リンク貼る程度にする。

まず、コアのアイディアや方針はほとんど同じ。 ただしSOAではサービス分割の指針が言及されていないとする記事がある。 他にはベンダー主導でトップダウンで分解ありきで構築すると指摘してたりする人もいる。

このうち前者についてはIDDD本ではSOAのサービスにサブコンテキストを割り当てるような指針を提示している。 SOAをDDDと組み合わせることでサービスの粒度についての指針を明確にしていた。

感想

SOAもMSAもほとんど同じで、自分達の組織やサービスをよくする判断基準や考え方を作るのが大事だと思う。 サービス分割の指針である 単一責任原則サービスの大きさは担当チームより小さくする は注意しておきたい。

組織構造や規則を自明のものとせず必要に応じて作り変えも視野に入れるべきということが、 マイクロサービス(MSA)で示されていて素朴に良いなぁと思った。

MSAのデメリットを緩和する原則やツールやミドルウェアが生まれている。 これらを使えず、チームの技術力や文化が合わないまま無理に進めると破堤する。

マイクロサービスアーキテクチャでは原則や周辺ツールの考え方などが紹介されている。 SRE本なども似たよう課題と戦うための実践書になっている。 ツールやミドルウェアについてはエコシステムの発展するようにCNCFが活動している。

comments powered by Disqus