これからの中規模分割型アーキテクチャを考えよう
さんざん言及されている資料であり、
これさえ読んでもらえれば、これ以降の文章は不要ですΣ(・ω・ノ)ノ
個人的に、モノリシックアーキテクチャと、マイクロアーキテクチャも、
どちらも「それだけでは辛い」ものであり、
どこかでバランスを取らないといけない・・・と、考えていました
それに対する明確な回答がこの資料であり、
少なくとも現時点においては最適解だと思っているので、
これ以降はただの自分用メモです...φ(・ω・`)
Case1:業務系CMSのAPI
私が最初に「API状のもの」を作った時*1のアーキテクチャがこちら*2です(´・ω・)っ
当時、私は関数型言語に興味があったので、
アーキテクチャの設計そのものにも関数型的概念を突っ込んでいました
www.slideshare.net
API層が「副作用がある(Store)」と「副作用がない(Logic)」に明確に分かれており、
Perlで表現されたViewがLogicのAPIのみを触るという、
「ちゃんと分離されている感のある」のが非常に良かったと思います
問題は、Logic層がViewに強く依存しているところです
そもそも、Perlで書く量を限界まで減らそうとした結果、
Logicに業務ロジックのほとんどが移動した・・・というのが真相なので、
Logicにエラー文言のようなViewに関わる情報まで含んでいるのが問題です(´-ω-)
とはいえ、Store層はそこそこリソース単位のAPIとして成立しており、
Logic層も今回の機能に特化したAPI群である・・・と考えると、
(Viewの情報さえ分離できれば)構造そのものは不適切でもなかったと思います
Case2:スマホゲーのAPI
急にシステムが飛びますが、次に設計したのがスマホゲーのサーバですΣ(・ω・ノ)ノ
スマホゲーももちろんWebシステムではあるのですが、
通常のWebシステムに比べて、とがった部分が多々あります
- セキュリティレベルを相当高くする必要がある(例:通信データ自体を暗号化)
- 応答を少しでも早くしないといけない
- ユーザーデータが莫大で、単一スキーマに存在することが必須(意味で分離できない)
- 業務ロジック(=ゲームルール)が複雑で、多数のリソースを同一トランザクションで処理する必要がある
- 通信のリトライを雑にすることが許されない
- etc...
「ま、ここはいっか」って妥協が許されないのがゲームサーバです(lll゚Д゚)
(ユーザーが不正を働くモチベーションが非常に高いため)
にもかかわらず、「ゲームの売り上げ」と「サーバの精度」はあまり相関がなく、
結局のところ別な要因で決まるのも辛いところで、だからこそサーバエンジニアは、
企画やクライアントエンジニアが動きやすい環境を作るのが仕事になります
そんな当時のアーキテクチャは、こんな感じでした(´・ω・)っ
莫大なユーザーデータが単一のスキーマに存在するのが必須なのですが、
その代わりユーザー(のID)で水平分割はしやすいです
(その代わり、「ユーザーをまたいだ処理」*3で地獄を見ます)
また、ゲームロジックが複雑で、処理が多岐に渡るため、
そのロジックをどこに置くかが問題で、当時はmodelに置いていました
その結果、modelが複雑で面倒な構造になってました(lll゚Д゚)
また、管理画面も同一のソースで管理していたのですが、
これは管理画面からの操作でも、ユーザーデータのバリデーション*4などは同一のため、
開発効率を考えると仕方なかった部分はあります
今考えるともうちょっとやり方はあったと思いますが、
小人数で面倒なシステム開発を進めるためにも、
ある程度モノリシックなシステムになってしまうのも仕方ないのかな・・・と
あと、これは当時の会社の方針ですが、
どうしても「アーキテクチャの設計」ができるエンジニアは限られるため、
ある程度プロジェクトが軌道に乗った段階で、別の新規プロジェクトに回されました*5
そのため、「最初から最後まで」見ていたシステムはなくて、
そこはちょっと残念です・・・が、
私のようなタイプが「運用フェーズ」に向いてないのも事実なので、合理的です
Case3:モノリシックなAPIとView
そして現状いじっている業務システムです
Case1とCase3の中間を取ったようなアーキテクチャになってます
以前に比べるとAPIやRailsに関する理解も深まったので、
かなりリソース志向のAPIになっており、
さまざまなViewでそれを使う設計になってます
いわば、リソースAPIがいろいろなシステムのデータ管理層になっており、
View層がロジカルな部分を担当し、画面を作る・・・という形です
最初はReadがメインだったので問題なかったのですが、
Writeが発生したあたりでだんだん粗が見えてきて、
Viewがどんどん膨らんでしまいました(lll゚Д゚)
また、API側にも業務ロジックが一部入り込んでしまったりと、
Case1のLogicにあたる「業務ロジックを管理する層」を明確にしなかったのが失敗です
今にして思えば、私はCase1を「いまいち」だと思っていたのですが、
その理由を明確に分析してなかったのかな・・・と
実はCase1を整理したバージョンが正解に近かったのですが・・・(´・ω・`)
未来の話:これからの理想的アーキテクチャ
冒頭のスライドの内容を抜粋すると、こうなります
- Railsはリソース志向に特化したフレームワークである
- 結果的に小規模のリソース群に対しての開発効率がダントツである
- しかし、業務ロジック層を無理矢理入れようとした結果、システムが複雑化する
- 先の例ならmodelがファットになる
- 最近の例だとService層を導入する
- Railsの良さを生かしつつ、全体のアーキテクチャでカバーできないのか?
これに加えて、最近のマイクロアーキテクチャが、
「細かすぎてわからなくなってしまう」という問題を抱えているのもあります
「巨大なシステム」も「数が多すぎるシステム」も、
結局のところ「人間の認識限界を超える」のが問題なのです
設計の肝は「人間の認識できる規模にインターフェースを整理すること」ですからね
うまく多層化されたシステムや、うまく構造化されたコードは、
ある処理を考える時に、「その先」のことを考えず、
「今見ている処理」を小さく考えることができます
関数型言語(的な設計)の肝はそこにあるのですが、
本質的な話ばかりが先行した結果、
非常に扱いづらい印象になってしまったのがとても残念です(´-ω-)
先のスライドをふまえつつ、私が理解した範疇で、
もし「次」を考えるとすると、こうなるのかなと
- View-Logic(Service)-Resource-Dataに層を分ける
Data-Resouceの層を組むのにRailsは便利で、
その範疇であればRailsが破綻しないのであれば、
こういうやり方になるのかな・・・と...φ(・ω・`)
本来、全てのデータ処理に対してトランザクションを張りたいところですが、
それでは(Case3のような)巨大なスキーマができあがり、
管理できなくなるので、ある程度整合性をあきらめて分割する部分も出てくるのかなと*6
まとめ
個人的に「モノリシックなアーキテクチャ」も、「マイクロアーキテクチャ」も、
どちらもどこかで破綻すると思っていて、
「ちょうどいい落としどころ」がどこかにあるだろうな・・・と思ってました
その指針を明確に示しているのが冒頭のスライドであり、
非常に価値があると思います
ぜひ皆さんも読んでくださいね(`・ω・´)
*1: 厳密にはその手前があるのですが、ここに残すような精度のものではないし、結局リリースされていない=運用されてないので、価値はありませんΣ(・ω・ノ)ノ
*2: どこかで似たような図を書いて発表した記憶があるのですが、プライベートな勉強会で資料をUPしなかった気がするので、作り直しました(´-ω-)
*4: しかもめっちゃ複雑(lll゚Д゚) 「お金を増やす」だけでも多数のバリデーションが必要ですからね・・・
*5: 別な人に開発を引き継ぐのが前提だったため、「できるだけわかりやすい、Railsの標準的な設計」を心がけたので、ここでRailsに対する習熟度が上がったと思ってます
*6: もちろん、Case2のようなゲームサーバの場合、ユーザーデータは単一トランザクションでないとダメなので、このような設計は採りづらいと思います。業務系寄りの話ですね