目次


マイクロサービスへのリファクタリング: 第 1 回 モノリスから移行する際の考慮事項

従来型のミドルウェア・アーキテクチャーからマイクロサービスへ移行する方法

Comments

マイクロサービスの話題は、クラウド・プログラミング・コミュニティーでもアジャイル・プログラミング・コミュニティーでも持ちきりのようです。かつては REST などの他のアーキテクチャー理念が開発の世界に旋風を巻き起こしましたが、今はマイクロサービスが最新の時代の波としてその頂点に達しようとしています。けれども、マイクロサービスとはいったい何なのでしょうか? Java 開発者にとって、なぜマイクロサービスが重要なのでしょうか?

このシリーズではこれらの質問に答え、アプリケーションをマイクロサービスに移行することを選びたくなる理由を説明します (第 1 回)。また、データ・リファクタリングについて詳しく探り (第 2 回)、皆さんがマイクロサービスに移行できるよう、ステップ・バイ・ステップ方式の移行について説明します (第 3 回)。

マイクロサービスとは何か?

マイクロサービスを主題とした Martin Fowler と James Lewis の優れた共著に、最もわかりやすいマイクロサービス・アーキテクチャーの定義が記述されています。その定義によると、マイクロサービス・アーキテクチャーとは「それぞれ独自のプロセス内で動作する小さなサービスの集合体としてシステムを構成し、それらのサービス間では軽量のプロトコルを使用して通信を行うアーキテクチャー手法」を指します。ここで理解すべき極めて重要な概念は、各マイクロサービスがそれぞれ固有の 1 つのビジネス機能を表すことです。

Fowler はマイクロサービスを「service orientation done right (サービスを正しく位置付けること)」とも呼んでいます。Fowler と Lewis が述べているように、そして他の多くの人々が証言しているように、エンタープライズ・コンピューティングのランドスケープには今となっては失敗に終わった大規模な SOA プロジェクトの残骸が散乱しています。マイクロサービスは、この SOA のトレンドを覆すのに役立つ可能性があります。けれどもそれを実現するためには、マイクロサービスをどこに適用すべきかを理解することが必要です。そしてさらに重要な点として、マイクロサービスは、きちんと整理されていない、巧みでも画期的でもないプロジェクト内でとりわけ効果を発揮するということを認識しなければなりません。

マイクロサービスにリファクタリングする理由とは?

開発者たちの望みに反し、すべてのアプリケーションがグリーンフィールド・アプリケーションであるわけではありません。企業には既存の Java コードが大量にあり、多数の Java 開発者を抱えているのが現状です。既存の Java コードのすべてを投げ捨て、新しいランタイムと新しいプログラミング言語を使用して新たなスタートを切るのは、経済面だけをとっても実現可能ではありません。

それよりも賢明なのは、コードの中から有効な部分を見つけて、適切なフレームワーク内でその部分を再利用することです。このことから、既存のシステムを稼働させたまま、より持続可能な開発モデルに移行させるには、既存のアプリケーションをマイクロサービスにリファクタリングすることがほとんどの場合に最善かつ堅実な手法となります。

マイクロサービスにリファクタリングする方法

「リファクタリング」という言葉は何を意味するのでしょうか?プログラミング・コミュニティーでは、リファクタリングとは、「コードの振る舞いを保ちつつ、内部構造を変えること」と定義しています。これは突き詰めれば、外部 API を現状のまま維持しつつ、コードの動作方法やパッケージ化方法を変更するということです。したがって、マイクロサービスへのリファクタリングとは、アプリケーションにマイクロサービスを追加するとしても、必ずしもアプリケーションを変更する必要はないことを意味します。この場合、アプリケーションのパッケージ化方法、そしておそらく API の表現方法を変更するのであって、新しい機能をアプリケーションに追加するのではないからです。

マイクロサービスにリファクタリングすることは、すべてのアプリケーションにとって正しい答えとなるわけではありません。また、常に上手くリファクタリングできるとも限りません。けれども、すべてのコードを捨て去ることができる場合を除けば、リファクタリングするかどうかを検討する価値はあります。基本的な考慮事項には、以下の 3 つがあります。

  • アプリケーションがどのようにパッケージ化 (およびビルド) されているか
  • アプリケーション・コードはどのように機能するか
  • バックエンドのデータ・ソースがそれぞれ異なる形で構造化されている場合、アプリケーションはそれらのデータ・ソースとどのようにやりとりするか

ステップ 1. アプリケーションをリファクタリングする

最善の出発点は、Java アプリケーションのパッケージ構造を再調査して、コードの変更を開始する前から新しいパッケージ化方法を導入することです。2000 年代のはじめの頃、私たち開発者はこぞって、論理アプリケーションをまるごと収めた、かつてなく大きな EAR ファイルを作成するようになりました。そのような EAR を企業内のあらゆる WebSphere Application Server にデプロイしたわけです。問題は、これによって、アプリケーションに含まれるコードの各部分が、同じデプロイメント・スケジュールおよび同じ物理サーバーに結び付けられたことにあります。何かを変更するということは、すべてのコードを再テストすることを意味するため、コードを部分的にでも変更するにはあまりにもコストがかかりすぎて、検討の余地はありませんでした。

けれども現在は、Docker や PaaS などのコンテナーと WebSphere Libertyのような軽量の Java サーバーによって、経済面は変化しています。したがって、パッケージ化を再考することができます。適用し始めなければならない原則には、以下の 3 つがあります。

  1. EAR を分割する: 関連する WAR のすべてを 1 つの EAR にパッケージ化するのではなく、EAR を独立した複数の WAR に分割します。それには、コードにマイナーな変更を加えなければならない場合もあります。また、アプリケーションのコンテキスト・ルートを変更するとしたら、おそらく静的コンテンツを変更する必要が生じてくるはずです。
  2. 「サービスごとのコンテナー」を適用する: 次に、「サービスごとのコンテナー」パターンを適用し、できればそれぞれ固有のコンテナー (Docker コンテナーまたは Bluemix インスタント・ランタイムなど) にデプロイされた個々の Liberty サーバー内に各 WAR をデプロイします。このようにすれば、各コンテナーを単独でスケーリングできるようになります。
  3. 個別にビルド、デプロイ、管理する: WAR が分割されていれば、自動化された DevOps パイプライン (IBM Bluemix Continuous Delivery サービスの一部となっている Delivery Pipeline など) を使用して各 WAR を個別に管理できます。これは、継続的デリバリーを利用することを目的としたステップです。

以下の図に、以上の 3 つの原則を適用した場合の効果を示します。

EAR が分割される前と分割された後を示す図
EAR が分割される前と分割された後を示す図

ステップ 2. コードをリファクタリングする

デプロイメント戦略が個々の WAR レベルで適用されるようになったら、WAR をより粒度の細かいレベルにリファクタリングする機会を探します。マイクロサービスを個別にパッケージ化する手法に対応できるよう、コードをリファクタリングする機会を見つけられるケースは 3 つあります。

  • ケース 1: 既存の REST または JMS サービス: このケースは、リファクタリングするのが断然簡単なケースです。既存のサービスに、マイクロサービス・アーキテクチャーとの互換性がすでに備わっている場合もあれば、互換性を持たせることが可能な場合もあります。まず、各 REST サ-ビスまたは単純な JMS サービスと残りの WAR との関連性をなくして、各サービスを固有の WAR としてデプロイします。このレベルでは、サポート JAR ファイルが重複していても問題はありません。ここでも同じく、重要となるのはパッケージ化であるためです。
  • ケース 2: 既存の SOAP または EJB サービス: 既存のサービスは、おそらく関数型手法 (Service Façade パターンなど) に従って作成されていることが考えられます。関数に基づくサービス・デザインであれば、通常は、アセット・ベースのサービス・デザインにリファクタリングできます。大抵、Service Façade パターンによる関数は、元々は単一オブジェクトに対する CRUD (Create, Retrieve, Update, and Delete) 処理として作成されているからです。これに当てはまるとしたら、RESTful インターフェースに簡単にマッピングすることができます。つまり、EJB セッション Bean インターフェースまたは JAX-WS インターフェースを、JAX-WS インターフェースとして再実装すればよいだけです。その際にオブジェクトの表現を JSON に変換しなければならない場合もありますが、通常、この変換は非常に難しいというわけではありません。特に、すでに直列化のために JAX-B を使用している場合は比較的簡単に変換できます。
    単純な CRUD 処理一式でない場合 (例えばアカウント転送) でも、Command パターンのバリエーションを実装する RESTful サービスを構成するために適用できる多数の手法があります (例えば、/accounts/transfer といった単純な関数型サービスを作成します)。
  • ケース 3: 単純なサーブレット/JSP インタ-フェース: 多くの Java プログラムは、実際にはデータベース・テーブルの単純なサーブレット/JSP フロント・エンドにすぎません。Active Record パターンなどのデザイン・パターンに従っている場合は特にそうですが、「ドメイン・オブジェクト」層と呼ばれるものが一切ないこともあります。その場合に有効な最初のステップは、RESTful サービスとして表すことができるドメイン層を作成することです。ドメイン駆動型デザインを適用してドメイン・オブジェクトを識別すると、足りないドメイン・サービス層を特定するのに役立ちます。ドメイン層を作成した後 (そして新しいサービスのそれぞれを独自のWAR にパッケージ化した後) は、新しいサービスを使用するように既存のサーブレット/JSP アプリをリファクタリングできます。あるいは、(JavaScript、HTML5、および CSS を使用するか、ネイティブ・モバイル・アプリケーションとして) まったく新しいインターフェースを作成するという方法も選択できます。

ステップ 3.データをリファクタリングする

上述の 3 つのケース内で定義した小さなサービスを作成して再パッケージ化した後は、マイクロサービスを導入する上で最も難しい問題となるのは何かという点に注意を向ける必要があります。この課題についてはシリーズの第 2 回で詳しく調べますが、単純なケースの場合に適用できるルールがいくつかあります。

  1. データの孤島: まず始めに、コードが使用するデータベース・テーブルに注目します。使用されているテーブルが他のすべてのテーブルから独立している場合、あるいはリレーションシップによって少数のテーブルと結合された小さな孤立した「島」という形になっている場合は、それらのテーブルをデータ・デザインの残りの部分から単純に切り離すことができます。その後は、サービスに適した選択肢を検討することができます。

    例えば、SQL を引き続き使用するか、それとも重量級のエンタープライズ・データベース (Oracle など) からサイズの小さい自己完結型のデータベース (MySQL など) に移行するかを検討します。あるいは、SQL データベースを NoSQL データベースで置き換えるという選択肢もあります。どの選択肢を選ぶかは、データに対して実際に実行しているクエリーの種類によります。実行しているクエリーの大部分が「主」キーに対する単純なクエリーであれば、キー・バリュー型データベースまたはドキュメント指向データベースが大いに役立ちます。一方、大きく異なる複雑な結合を行っているのであれば (例えば、クエリーが予測不可能である場合)、SQL を使い続けるのが最善の選択肢となるでしょう。

  2. バッチ・データ更新: テーブル間のリレーションシップがわずかな数しかなく、いずれにせよデータを NoSQL データベースに移行することにした場合は、既存のデータベースへのバッチ更新を行う必要があるかどうかを検討する必要があります。テーブル間のリレーションシップを調べると、リレーションシップに時間的要因が考慮されていないことがよくあります。つまり、常に最新の状態にする必要はないということです。そうであれば、多くの場合、数時間ごとにデータをダンプ/ロードするという手法で問題ありません。
  3. テーブルの正規化解除: 他のテーブルとのリレーションシップが数多くある場合は、テーブルをリファクタリング (DBA の用語では、「正規化解除」) できる可能性があります。正規化解除を話題にするだけでも、多くのデータベース管理者の反感を買うはずですが、一歩譲ったとしても、チーム全体で、データが正規化されたそもそもの理由を考えなければなりません。スキーマが高度に正規化されている理由が、重複を削減してスペースを節約するためである場合は珍しくありません。ディスク・スペースには大きな費用がかかっていたためですが、今はそうではありません。今は、ディスク・スペースではなく、クエリー時間を最適化しなければなりません。それには、正規化解除が最も簡単な手段となります。

まとめ

このチュートリアルでは、マイクロサービスへのリファクタリングとは何か、そしてマイクロサービスへの移行手法を選択する際に考慮すべき要因について説明しました。幸いなことに、コードをリファクタリングするのは思っているほど難しいことではありません。実際のところ、大抵はかなり単純な作業です。(比較的単純にリファクタリングできるコードを見つけるためにコードをひと通り調べると、複雑なコード・セクションはごくまれにしかないことがわかる場合があります。

このシリーズの第 2 回では、アプリケーションへのマイクロサービスの導入方法の選択に、データの構造がどのような影響を与えるかを深く探ります。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Cloud computing, Java technology
ArticleID=1055497
ArticleTitle=マイクロサービスへのリファクタリング: 第 1 回 モノリスから移行する際の考慮事項
publish-date=12212017