導入にあたってどのような工夫をしてきたのかを紹介しました。そこに書かれていたように、新しいプログラミング言語を導入するにはそれなりの整備コストがかかります。導入したのでしょうか。導入した背景について説明していきたいと思います。どのプログラミング言語を採用するかや、どのようなアーキテクチャを選定するかというようなことは非常に影響範囲の大きい決断になるため、会社全体の技術的・事業的なコンテキストと切り離しては語れません。技術的・事業的な背景について、この後の話をするために必要最低限の部分を紹介したいと思います。サーバーで全てのサービスを作っていました。書ける、という状態でした。使うことに意味があったのでしょうか(適切な技術選定であるとは、そこに根拠があるということで、この文章はGoの導入を根拠付けるためのものです)。比較的大きめのサービスをリリースする際に変更を行ってきました。導入と関係しているため、話をそこから始めたいと思います。開発時に行われました。サーバーを立てる、という変更です。サービスに対してそれぞれ対応するメインサーバーが存在する、という状況になりました。サービスでかつ違う開発チームなのだからサーバー/コードベースを分けよう、ということがあったと思います。この変化をどのように捉えるかですが、これはアーキテクチャとしてはマイクロサービスというよりはマルチサービスと呼んだ方が良いものであると思います。サービスに対して一つのサーバー(サービス)を用意する、という意味ではサーバーの粒度自体は細かくなっていないためです。事業が増えたので横並びにサーバーが増えた、ということです。いう風に考える方が、この後の変化との比較をする上で分かりやすくなるため、この変化はマルチサービス化とします。その上で開発しています。いう新規事業の開発と同時に行われました。アーキテクチャ図です。バックエンド全体を構成しています。[^1]ではなぜこのようなアーキテクチャになったのでしょうか。マイクロサービスでした。名刺管理のアプリケーションとして開発されました。この名刺管理というのは、問題として抽象化すると、入力は写真で、出力は名前や会社名などに構造化されたプロフィール情報のリストになります。これをどのように解くかということですが、複数のサブタスクに分解します。すると、これらそれぞれは機械学習を使って解く類の問題になります。言い換えれば入力と出力が明確であるということで、つまり関数としてのコンポーネント化が可能です。あとで述べますが、ソフトウェア・コンポーネントの単位として妥当であることがマイクロサービスの基本的な条件であり、これはマイクロサービスの条件を満たしています。これがマイクロサービス・アーキテクチャにする必要があった大きな理由の一つです。分かりやすくするため、先ほどの図に実装言語を重ねると以下のようになります。アプリケーション全体を見たとき、機械学習タスク以外にもコンポーネント化できるドメインがいくつかありました。通知サービス、設定管理サービス、トラッキングリンク発行サービスといった具合です。開発開始以前からここまでにありました(配布した冊子の前半に当たる内容です)。サーバーもありました。それらのサーバーを紹介してみたいと思います。線分はあくまでイメージです。いう依存関係を持つことが多いですが、例外もあります。作って成功したのは、通知サービス(notifications)、設定管理サービス(options)、トラッキングリンク発行サービス(link-tracker)のように単一の責務を持ってきちんと抽象化されたサーバー群でした。利用するユーザーが意識すべきデータの種類も片手で数えられる程度のものです。先ほど、ソフトウェア・コンポーネントの単位として妥当であることがマイクロサービスの基本的な条件であるという観点を提示しました。ソフトウェア・コンポーネントは明確な責務を担いきちんと抽象化されていることが望ましいです。書くのか、という話が同時に生まれます)。混同されがちですが「マイクロサービス=コードベースが小さい」ではないということを補足しておきます。経由してデータを受け取り、複数のタスクを同時並行処理するといった多少高度な仕組みを備えています。これらの実装は抽象化によって隠蔽されているため、使う側の視点で言えばシンプルに保たれています。小さくてコードが多いのであればそれは複雑性を抑えながら多くの仕事をしていて価値がある可能性があります。ここでは、きちんと限定された責務を持っているということをもってマイクロサービスと言っています。作って良かったマイクロサービスには大きく二種類あります。渡したりと言ったアグリゲーションを行う場合です。全体を見ると一定数あります。いずれにしても、きちんと責務が定義されてインターフェイスだけで区別できるサーバーであるという意味では利用する側は大きく区別はありません。作って成功しなかったサーバーとはどのようなものでしょうか。書こうとしていました(アーキテクチャ図における中央のサーバーです)。ここで言うメインサーバーというものが何を持って他と区別されるかというと、その事業をやっている開発チームのデフォルトの実装対象となるもの、に該当するかどうかです。ユーザーにメールを送るというような雑多な施策があったとき、妥当なマイクロサービスがなければそれを行うのはメインサーバーの役割になります。いうメインサーバーを作ったのですが、結果として見るとそこにコードは追加されず、デフォルトの実装対象にはなりませんでした。サーバーに色々追加される形になりました。書くと言う試み自体は成功しませんでした。慣れていなかったというような解消可能な理由を除いても、本質的な理由は動くものを作るまでのリードタイムだったと思います。ことをやるのは難しいというのが試した結果の結論でした。そこまでの速度は出せません。書くようにしています。書くのが難しいとして、メインサーバーなるものは必要なのか、というような話はあると思います。これについては、次のように考えています。前提として、新規サービスでは施策を次々とリリースする必要があり、またどのようなドメインを扱うかやそこに含まれるモデルといったものも時々刻々と移り変わっていきます。マイクロサービス・アーキテクチャでこの不確実性にどのように対処するのが良いでしょうか。これに対する対処の極端なものとして2つ考えられます。一つは、マイクロサービス・アーキテクチャ全体のレベルで調整する方針です。具体的に言うと、とあるマイクロサービスの責務を拡張したり、新規のマイクロサービスを作ったり、と言ったことを都度行なっていきます。一つは、どのようなマイクロサービスにも該当しないと判断した場合のデフォルトの実装対象を決定しておき、迷った場合はそこに実装する。その上で、一つの独立したマイクロサービスとして成立することが十分に確認できた段階や始めからそう確信できる場合にマイクロサービスにする、というような方法です。現実にはこの中間のところでバランスを取ることになりますが、どのポイントを取るにしろ未来のサービスの仕様がユーザーの反応や外的環境など予測不可能な要因にも依存する以上、ある時点でアーキテクチャが決定できないというようなことは確実にありえます。この時メインサーバーのようなものがないとどんどんと過度にマイクロサービス化をしていくことになり、例えば一つの変更をするのに多くのマイクロサービスに手を入れなければならない、というような事態が起きます。逆に言えば、デフォルトの実装場所というものを用意しておくことでが全体のアーキテクチャの健全性を支えるわけです。積極的に書くべきところとそうでないところが見えてきます。Python」など特定の言語で書くことを強く要求しないマイクロサービスです。方に軍配が上がります。いうライブラリのコンビネーションから来ています。2019』などをご参照ください)。問題は単にライブラリやフレームワークの有無なのでしょうか。ここには技術選定という観点でもう少し踏み込んで考察すべきポイントがあるように思います。どのようにしてそのようなライブラリ(もしくはフレームワーク)を作るか、ということを考えるのが良さそうです。違いが言語の特性に由来するのであれば、対処は違ったものになります。異なる言語を組み合わせて使う、というようなアプローチです。ライブラリなどのエコシステムは時間が経つと変わりえますが、プログラミング言語の特性というものは原則としてそこまで変わることがないためです。マイクロサービスを書くと良い理由です。静的型付け言語であり、これに言語仕様のシンプルさが加わりコードリーティングやちょっとしたマイクロサービスの挙動変更が他言語をメインとするプログラマーでも比較的容易に行えます。同様に静的型付け言語であることからメモリ効率や空間効率に優れています。詳しくはコラムで触れていますが、マイクロサービスにするモチベーションとして特定ドメインで高いパフォーマンスが要求されるというようなことはあるため、並行処理機構と相まってマイクロサービスに適しているケースが多くなります。使ってインターフェイス駆動で開発することが非常にフィットします。使うことで、適切な抽象化が行いやすくなりマイクロサービスとしても適切なものを作りやすい、ということがあります。代替が出来ないのか、ということの理由です。静的型付け言語はエラーを検出してくれるので動的型付け言語より良い、という単純化された話が出ます。書いていこうという時期にその大きな理由の一つとしてもそれが挙げられていました。これは状況によっては真なのですが、このようなケースにおいては、静的型付け言語と動的型付け言語の特性を考えた上で、達成したいことが何なのかをよく考えた方が良いでしょう。静的型付け言語はコンパイル時に多くの性質を検査します。これによって多くの凡庸なミスから生まれるエラーを検出できます。それは言葉を変えれば「正しいプログラムでないと走らせることができない」ということを意味します。ような極端に非常に型の制約が強い言語で開発を行ってみるとこのことは明瞭に理解できるように思います)。動的型付け言語はその逆で、間違っているプログラムであってもとりあえず走らせることができます。従って、仕様がかっちり決まったものに対しては(仕様の定義をコンパイラが要求するので)静的型付け言語はワークしやすいですし、プロトタイピングが重要な役割を果たす開発においては動的型付け言語には一定の価値があります。したがって、「そもそもどのようなものを作るか」が焦点となっており、かつそれを明らかにする方法がプロトタイピングであれば、動的型付け言語の方が適している可能性がある、ということが重要なポイントの一つになります。もこの技術的特性を開発の生産性を高めるのに最大限利用しています。設計思想の違いと、そこから導かれるフレームワーク特性の違いという話があります。ミニマルであることを志向している言語です。いうものを開発しました、これは次節で話題にします)。開発者の楽しさを重視する言語であり、哲学はありますがミニマル志向ではありません。提供しますし、HTMLの記述からメールの送信までも行うフルスタックのフレームワークです。サーバー一つでアプリケーションを丸ごと作ることができます。サービス開発に使われてきたわけですが、これはマイクロサービス・アーキテクチャでは諸刃の剣の面があります。出来たマイクロサービスは単一の責務を負うもので、マイクロサービス・アーキテクチャはそのようなソフトウェア・コンポーネントの集合からシステムを構成することが必要です。いうのは何でも簡単に出来すぎてしまいます。端的に言えば「とりあえずここに入れておこう」というのが簡単なのですね(ただ先ほど述べたように、とりあえず入れておけるデフォルトの開発場所を作る、と言うのは一つの妥当な選択です。例外的にこれはメインサーバーとしては良い性質であることにも注意が必要です)。バージョンアップなどのメンテナンスコストも大きくなりがちです。マイクロサービスも作った場合の両方を、インターフェイスの規模感とアプリケーションコードの量と言う観点で示しています。サーバーサイドの主要プログラミング言語として採用しています。プロトタイピングなど漸進的な仕様の試行錯誤が必要は領域よりは、作りたいものが予め明確な領域であるマイクロサービスの実装に多く利用することにしています。余談ですが、このような見方をするとサービスメッシュというものが何なのかについても理解しやすいと思います(プロセスが分かれているが故にアプリケーションサーバーの実装言語に依存しない共通機能の括りだし、というフレームワークよりも更に共通化されたレイヤーとして見ることができます)。補足:マイクロサービスとして切り出すかどうかの判断よく、マイクロサービス・アーキテクチャではどのような単位でマイクロサービスにするのか、というような議題が挙がります。一定の言及をしていますが、ここで改めて少しまとめてみます。マイクロサービス・アーキテクチャというような格好の良い名前がついていたとしても、あくまでソフトウェアであることに注意した方が良いと思っています。オライリーの「マイクロサービス・アーキテクチャ」では「マイクロサービスはビジネス・ドメインで切る」というような話が出てきますが、これもあくまでマイクロサービス・アーキテクチャの応用形態の一つ、というくらいに捉えて、あまり教条的にならない方が良いと思います。一般的にソフトウェアは複雑なので、独立して理解できる部品に分けるという工夫が成されます。いった部品―ソフトウェア・コンポーネント―の単位の一つとして、マイクロサービスがあります。普通、ソフトウェア・コンポーネントに切り出すことは、何らかの抽象化を通じて行います。それが抽象ではなく具象、平たく言えば実装として理解されるのであれば、複雑さは減っておらず、ソフトウェア・コンポーネントとして切り出す意味がないからです。言うことで、マイクロサービスに切り出すかどうかの判断基準の第一は、それがソフトウェア・コンポーネントとして適切であるか、ということになり、その帰結として抽象として適切であるか、を考えることになります。抽象として適切であるかの一つの目安として、インターフェイスと実装の比率があります。狭いインターフェイスで多くの仕事をするようなものの方が得るものが多い、ということです(例えば、長針と短針を持つあの「時計」という抽象は、その裏で時刻を正確に刻む仕組みを全く理解することなくあのインターフェイスさえ理解していれば遍く利用できるので良い抽象であると言えるでしょう)。いうソーシャルグラフを扱うマイクロサービスがあります。提供することでインターフェイスはシンプルになるという見通しが立つので、ソフトウェア・コンポーネントとして適切であろう、という風に考えました。いう設計原則として紹介されています。言え、単にソフトウェア・コンポーネントにしたいだけであれば「ライブラリに切り出す」という操作もあり得ます。ライブラリを比較対象としたときの、マイクロサービスの特性はいろいろありそうです。思いつくままに挙げていきましょうか。言語非依存のプロトコルによって抽象化されているので、そのソフトウェア・コンポーネントを利用する側とその実装の間で異なる技術が使える、ということがあります。これは「技術的異質性」とか言われますね。計算資源がある程度独立であること、があります。ライブラリはあくまで使う側の計算資源に依拠して動作するため、ここは重要な違いです。isolation)が可能になります。これに関連して、デプロイ―ソフトウェア・コンポーネントのアップデート―はライブラリの場合は利用する側が明示的に行い、かつ利用する側のアップデートとして行われますが、マイクロサービスの場合はコンポーネントを提供する側が一括して行う形になります。別の言い方をすれば、複数のバージョンが同時に存在し続けるということがありません。これらのことをまとめて、僕はマイクロサービス・アーキテクチャという技術を、プログラミング言語非依存の通信プロトコルを採用した分散システム、と理解しています。存在し、それに対して一定の解決が成されています。一つの言語でやらない、と言うのは明記されても良い設計上の選択だと思います。言うことで、マイクロサービスに切り出すかどうかの判断基準の第二は、それが分散システムのコンポーネントであるべきか、ということになります。分かりやすいところで言えば、複数の利用者に対して一貫した動作を提供したい場合はマイクロサービスである方が良いですし、耐障害性などの動機もあり得ます。いうプラットフォームとして一貫して提供する必要があるため、ライブラリよりはマイクロサービスの方が望ましい選択でした。耐障害性と言う観点でも、ソーシャルグラフが一時的に利用できないケースでは、例えば「知り合いに関連した動作ロジックは作動せず一般的な動作ロジックが採用される」というようなフォールバック動作が考えられるでしょう。残る技術的異質性についてはどうでしょうか。僕は、これはマイクロサービスの切り出しについての判断とは割と独立した観点だと思っています。サーバーを立てれば良いだけだからです。いえ技術的異質性はよく話題に上がりますし、実際それが必要になったケースもありました。詳細を掲載しています)。言語の話をしたので、フレームワークの話にも少し触れておきたいと思います。マイクロサービスの実装言語として見出して使っています。grapiというものを作ったので、これの背景に触れたいと思います。そもそもなぜフレームワークが必要なのかというところですが、分かりやすい課題として次のようなことがありました。部分はバラバラでした。正確に言えば近い時期に作られたマイクロサービスは似た構成を持っているがどれも少しずつ違う、といったことでしょうか。いう土台となる構成があります。層を追加する、というような判断はそれぞれでありますが、土台があるのであくまでそこからの拡張として捉えることができます。これがないと、そもそもの書き方が実装者の力量に大きく依存してしまったり、また各々に力量があったとしてもコンセプトが違うのでコードリーディングの際に無駄なオーバーヘッドが生じていました。無駄な抽象化は嫌われます。フレームワーク化するというだけでは何を作るかは決まりません。ソフトウェアにはコンセプトが必要で、コンセプトを実現する設計上の選択が必要です。ここでは、そういった設計上の選択について、1)開発方法、2)責任範囲、3)設計の雛形の3点を採り上げてみたいと思います。して出し、それをレビューした後に実装に取り掛かります。これは「インターフェイス駆動開発」と呼ばれたりもしますが、いくつか重要な意味があります。レビューで引っかけることができます。Goで書かれるサーバーはマイクロサービスであり、マイクロサービスはAPIを通して抽象化されているべきなので、このチェック機構を強制することには意味があります。プロセスを縛るだけでは良い開発者体験とは言えず長く続きません。コード生成を行い実装の助けもになるような機能を統合しています。インターフェイス駆動開発というのは分かっているは最初から分かっていますが、人によっては開発の考え方を変える必要があるものなので、このようなガイドがあることによって比較的導入が容易になります。責任範囲:ミニマルスタックマイクロサービス、つまり責務が明確で少数のインターフェイスを持つサーバーに適切なフレームワークはどのようなものでしょうか。一度、メインサービスと比較した時のマイクロサービスの性質を載せます。この表から言えることとして、次のようなことがあります。持地、コードの追加頻度は少ないようなサーバーで、期待される動作までのリードタイムが長くて良いのであれば、フレームワークの責任範囲はミニマルスタックが良いということです。言えば、フレームワークの利用者に提供するインターフェイスも少ない方が良いということがあります。「フルスタック、インターフェイス多め」で置き換えてみたらどうでしょうか。責務が明確な小さいサーバーなのでフルスタックで提供されているインターフェイスやライブラリのほとんどは使われることはありません。したがって無駄なメンテナンスコストを払うことになります。提供されるスタックやインターフェイスによって機能追加が高速にできたとしても、その機能追加自体が時々しか行われず逆に利用される方が圧倒的に多いのであればそのメリットは少ないです。利用することが多いのであれば確実に動いて欲しいです。このような設計は既存の技術に対して相対的なものであり、技術的なポジショニングであると言えます。対して、ミニマルスタック、インターフェイス少なめであるように設計することで価値を上げている、と言えます。クリーンアーキテクチャGoの用途として、高いパフォーマンスが要求される部分を抽象化してマイクロサービスに切り出すとケースもありますが、grapiにはこのための工夫が少しだけしてあります。ようなライブラリは同梱せずに、サーバーの設計の雛形としてクリーンアーキテクチャを提供するという選択です。「ユーザー同士のソーシャルグラフを扱うマイクロサービス」のようなものを考えたときには、シンプルなデータ構造だが大量のデータを扱う必要があったり、データの検索がグラフのトラバーサルになったりと、必ずしもリレーショナル・データベースが適切とは限りません。裏で特殊なデータベースが使われていてそれが上手く目的の仕事を果たしてくれるのならそれは嬉しいということです。して抽象化を行うことは、単に特異なミドルウェアのことを知らなくて良いという使う側の視点だけでなく、データベース選定という設計上の決断を隠蔽することで利用データベースの変更に対してモジュラーに働きます。リレーショナル・データベースの利用と結合せずにクリーンアーキテクチャを採用しているのは、このような用途を見込んでいるためです。まとめまとめると、以下のような設計上の選択を行ったのがgrapiです。「言語」とその言語の「用途」をつなぐものとしてフレームワークを設計することで技術のポジショニングをより明確にしています。フレームワーク」の三つの観点から比較しました。全ての点に置いて相性の良い性質を備えているわけではないですが、あるプログラミング言語や既成のフレームワークといった技術と、異なるユースケースを整理した上で、技術の採用やフレームワークの作成といったことを行うようにしています。それによって、全体として価値を最大化することを意図しています。一つの考え方として技術選定の参考になれば幸いです。本ということでGo言語の周辺にまつわる話題を紹介しました。逆に紹介しなかった内容としては、例えばマイクロサービス・アーキテクチャ全体を適切にマネジメントするためのチーム構成、同アーキテクチャにおける新機能開発の際の開発プロセスなどがあります。この記事ではマイクロサービス・ネイティブなサービス開発においてどのような技術選択が妥当かという軸で述べましたが、たとえばモノリスの段階的な解体においては同一言語での実装という制約があることもあるため、もう少し応用的な根拠づけが必要になると思います。エンジニアウォンテッドリーでソフトウェア・エンジニアをしています。BlogWantedlyのエンジニアによる、テックブログです。開発者向け技術情報を中心に発信しています。Visit確定要發送?取消。