この記事はMicroservices Advent Calendarの17日目の記事です。前回は#0: マイクロサービスの構築プロセスのお話でした。
はじめに
マイクロサービス・アーキテクチャの話は多くありますが、実際にマイクロサービスを新たに設計・開発するとき、どのような手順を踏むか、と言ったことを扱った文献はそれほど多くない印象です。そこで、自身の経験を元に、マイクロサービスの設計をどのようにしているか、ポイントをかいつまんで説明したいと思います。
前回は、あるマイクロサービスの構築プロセスを外観しました。今回は、一歩踏み込んで、マイクロサービスの責務を定義する話になります。
デザイン・ドキュメントを書こう
新しいマイクロサービスを作る前に、1枚のデザイン・ドキュメントを書くべきです。まず、作ろうとしている マイクロサービスの目的(責務) を書きましょう。それ以上のフォーマットは設けていませんが、一言で言うとどうなるかをまず書くのが良いです。ものによって、機能的な定義の方が適切なこともあれば、ドメイン(領域)の定義の方が適切なこともあると思います。
デザイン・ドキュメントは他の人にレビューしてもらうと良いです。1) そのマイクロサービスの責務は明瞭に理解できるか?、2) 他の既存のマイクロサービスと責務が重複している箇所はないか?と言った観点は必須でチェックすべきでしょう。なぜなら、責務が曖昧だったり重複がある状態では、ある機能を理解・実装する際にどのマイクロサービスを見るべきかが判断できず、開発が困難になるからです。一度あるマイクロサービスに実装した機能は、そう簡単には別のマイクロサービスには動かせないので、この複雑性は持ち込むべきではないです。
コラム:Medium におけるマイクロサービスの原則
少し前に読んだ記事で、Medium ではマイクロサービスであるためには 1) 単一の目的を持ち、2) 疎結合で、3) 高い凝集度を持つ必要がある、と定義していました。上の話でレビューについて触れましたが、こういったソフトウェア・エンジニアリングの古典的な概念に落として理解しておくのも、レビューなどの際に有用でしょう。
Link Tracker の例
それでは例を見てみましょう。以下は、link-tracker というマイクロサービスのデザイン・ドキュメントです。かなりシンプルなことを一般的に行うため、責務の説明自体は非常に簡潔です。
責務
一言で: トラッキング可能な URL を発行し、ハンドリングする。より詳しく: トラッキングは、HTTP のリダイレクトを通して行う。
...
デザイン・ドキュメントは README からアクセスできるようにすべきです。また、GitHub のリポジトリ・ヘッダーに責務を書いておくのも良いでしょう。
Users Service の例
もう少し複雑性の高い例をみてみます。Wantedly プラットフォームのユーザーを管理するマイクロサービスの例です。
責務 - なにをやる service か
- Users Service は、Wantedly プラットフォーム共通のユーザー情報をドメインとして管理するマイクロサービス。
- Users Service は、「ユーザーの作成、更新、削除、取得」機能を提供する。具体的には、ユーザー情報を操作する API の提供や、pub/sub によるユーザー情報の更新通知を行う。- Users Service 以外のサービスは、ユーザー情報が欲しい場合には Users Service から情報を取得する。
- ドメインロジックが失われるので、DB へ直接繋いで取得する事は原則禁止する。例外は Visit で、歴史的経緯により直接 DB へ繋ぐ事を許容している。しばらくはモデルコードの共有でドメインロジックの整合性を担保するが、将来的には Users Service からの取得へと書き直す。
1点目がこのマイクロサービスの責務を簡潔に表わしています。補足の部分は人によっては冗長かもしれませんが、機能から理解する人には分かりやすいです。2点目は、このマイクロサービス特有の歴史的事情に言及していて、データベースに直接接続している状況が例外であることを明確にしています。
これで十分?
さて、最初に書くデザイン・ドキュメントはここまでで十分でしょうか?言い換えれば、このマイクロサービスに関わる人は、このマイクロサービスが持つべきドメインとそれ以外を、明瞭に識別できるでしょうか?例えば、ユーザーの投稿は、このマイクロサービスの管理対象か?という質問に答えているでしょうか。
実際には、歴史のあるドメインを扱うこのマイクロサービスの場合、もう少し踏み込んで定義する必要性がありました。
ユーザーと言ったとき、大きくは A, B の二種類が存在し、これを Users Service はこれを全て管理する。
A) Wantedly のサービスを使ってる人(本来的な意味での「ユーザー」)
- 登録してるユーザー
- サービスを利用してるけど登録してないユーザーB) Wantedly のサービスを使っていない人
- サービスを使ってない人と「ユーザー」を同じ形式で管理したいとき、前者に対しても ID を発行したくなる。
- 例:People で scan された名刺が指す人、Facebook API で取得したつながり情報が指す人A) の登録してるユーザーに対して、以下のようなものがユーザー情報となる。
- ログインに関わる情報 / e.g. メールアドレス、パスワードハッシュ、Facebook ID
- プロフィール、及びプロフィールについての可視性の設定 / e.g. 職歴、学歴
- Wantedly Platform の基本機能に関わる内部フラグ / e.g. visit_enabled, people_enabled
- Wantedly Platform 内でグローバルな設定値 / e.g. 使用言語、国
ここではユーザーと呼んでいるものが何なのかについては明確にした上で、ユーザー情報については例示という方法を採っています。暗黙的に、ユーザー投稿はこの4つのどれにも含まれないことを示唆しています。こう書くことで、少なくともこのマイクロサービスにユーザー投稿を入れるときには議論が必要であることは分かります。その時点で全てのことが完全に明瞭に定義できないようなドメインでは、詳細度を上手く調整しながら現実的に開発ができる責務を定義することも、重要なスキルになってきます。
コラム:マイクロサービス・アーキテクチャでは全体設計は考えなくて良いか?
マイクロサービス・アーキテクチャになると、モノリシックなアプリケーションに比べてそれぞれが自律的に開発ができるようになる、従ってお互いの設計のことは考えなくて良いので良いものだ、という話があります。これはアプリケーションが適切なマイクロサービス群に分割されている場合にのみ真であり、逆に言うとそこを決定するアーキテクトの役割は非常に重要になってきます。
つまり、アプリケーション全体を見る立場からみると、各マイクロサービス内部の設計や実装はモノリスと違って無関心でいられる一方で、最上位のアーキテクチャを決定するための設計はモノリスよりも圧倒的に重要であるという二重構造があるので注意が必要です。
次回は、マイクロサービスの責務の具体的な表現である、インターフェースについて書きます。