C#erがマイクロサービスを考える
Tip
2018年末に記載したドキュメントで内容が陳腐化しています。
はじめに¶
最近マイクロサービスに関して考える機会があったのでまとめます。
業務で実績があるようなものではありません、ポエムです。
マイクロサービスってなんぞ?¶
いわゆるバズワードと考えてもいいです。
具体的に何を満たせばマイクロサービスです、ってものではないですし、正解があるわけでもありません。
それぞれの会社/チームごとに異なるのが自然ですが、実際に業務として行う場合は明確な定義を決めた方がいいでしょう。
上司に説明するときは、小さいサービスを作ってそれを組み合わせてアプリケーションを作ります、ぐらいでごまかしましょう。
マイクロサービスにすることでのメリットやらなんならはググればいくらでもでてくるので触れません。
と言いつつちょっと変わった観点個人的にうれしいポイントはこんな感じでしょうか。
- サービス単位で異なる技術が利用できるため技術検証目的で好きな技術が選択しやすい
- 単一のサービスとしてみたときに規模が小さいため、別のチームにxxxな人員がいても足を引っ張られない
サービスの単位はどう決める?¶
マイクロサービスの場合、ドメイン駆動設計 (DDD) で進めることを必須と考えていいでしょう。
最終的に巨大なシステムを作ることを目標とした場合、それぞれのサービスを境界付けられたコンテキストとすると説明がしやすいです。
技術者以外 (上司や顧客) に説明する場合はこれでいいと思いますが、技術者的にはもっと細かいことが気になるのでこのあと掘り下げます。
境界付けられたコンテキストをマイクロサービスとした場合、これは論理的なサービスであり実装に落とし込む際にはもっと細かい定義が必要です。
レイヤー¶
ドメイン駆動設計でのレイヤーは以下のようになります。
- UI層
- アプリケーション層
- ドメイン層
- インフラストラクチャー層
物理的なサービスとしてはレイヤーごとに別のサービスとして定義するとわかりやすいです。
が、この時点で非技術者が考えるマイクロサービス (境界付けられたコンテキスト) と技術者が考えるマイクロサービス (各レイヤー) で異なってしまうんですが、どう表現すればいいんでしょう? 誰か教えてください。
UI層¶
エンドユーザーが実際に触る箇所です。
webアプリの場合はhtmlとJavaScriptと考えれば良いです。
技術者以外 (上司や顧客) が見たときに一番わかりやすい箇所なので、頻繁に仕様変更が発生します。
仕様変更を抑えることは無理なので、UI層はあきらめて仕様変更を受け入れられるようにした方が良いです。
アプリケーション層¶
UI層から直接呼び出される箇所です。
webアプリの場合はRESTなAPIサーバーが多いでしょう。
ビジネスロジックは含めません。
ドメイン層¶
ビジネスロジックです、アプリケーション層から呼び出されることもあればドメイン層の別のサービスから呼び出されることもありますがUI層からは呼び出されません。
Webアプリの場合でもJavascriptから呼び出されることを考慮する必要がないため、RESTなAPIよりパフォーマンスを考慮した方がいいでしょう。
C#er的にはgRPC、具体的なライブラリとしてMagicOnionを検証したいところです。
インフラストラクチャー層¶
永続ストアとのやりとりを行う箇所です、ドメイン層からのみ呼び出されます。
DBだったりファイルだったり別の外部サービスだったりを対象に、ビジネスロジックは含めず純粋にCRUD操作のみを行うサービスとします。
リポジトリはどうわける?¶
技術者的に非常に悩ましいです。
できる限り細かくレイヤーごとにリポジトリを分けましょう、としたいところですが、実装レベルで考えるとレイヤー間で共有コードがでてきます。
具体的にはアプリケーション層とドメイン層でMagicOnionを利用したgRPCで通信した場合でも、intarfaceと送受信するデータ (DTO) を共有する必要があり、それをどこに置くかが問題です。
アプリケーション層のプロジェクトからドメイン層のプロジェクトを参照するのはイケてないので、intarfaceとDTOのみを定義したプロジェクトを作成しそれぞれのプロジェクトから参照させる、とするのが無難かと思いますがレイヤーごとにリポジトリを分けた場合はどこに置きましょう?
また、現実的に一から新しいシステムを作る場合は1つの機能を作る場合、単一のレイヤーのみの実装をすることは少ないかと思います。
一連の作業タスクの中でアプリケーション層とドメイン層を同時に修正する場合、リポジトリが分かれていると純粋に手間が増えます。
ということを考えると境界付けられたコンテキスト単位でリポジトリを作りたくなりますが、その場合でもデメリットはあるわけで。。
マイクロサービスではCI/CDを行うことが前提です、PullRequestを作成したときにCIすることを考えた場合、リポジトリが分割されていないとソースコードのpullに余計な時間がかかるようになりますし、修正対象でないプロジェクトをビルドするのは無駄でしかありません。
CI/CDツールをうまいこと構成してPullRequestの修正対象ごとに適切な処理を行うことができれば解決する範囲ではありますが。
正解はないのでそれぞれの会社/チームごとに検討するのがよさそうです。
個人的にはあとで分割するのは大変なので、最初に細かい単位でリポジトリを分割、リポジトリ間の参照はサブモジュールを利用するのがいいんじゃないかと。
TODO¶
- 技術選定
- 具体的な実装例