以前の記事では、コードをマイクロサービスベースのアプローチにリファクタリングする要因について触れました。最後に触れた側面の一つは、データをどう扱うかという問題でした。大規模なエンタープライズアプリケーションでは、これはしばしば最も厄介な問題であり、より深く扱う価値があります。私たちが目にしたのは、コーディングの問題なのか、コーディングの問題を装ったデータ・モデリングの問題なのかを判断するのが難しい場合があるということです。
これらのケースのいくつかを見て、リファクタリングコードを簡素化・改善するために取れるデータ・モデリングの選択について話します。しかし、その前に、既存のエンタープライズ・アプリケーションをマイクロサービスにリファクタリングするチームがよく尋ねる最初の質問に対処する必要があります。
IBMニュースレター
AI活用のグローバル・トレンドや日本の市場動向を踏まえたDX、生成AIの最新情報を毎月お届けします。登録の際はIBMプライバシー・ステートメントをご覧ください。
ニュースレターは日本語で配信されます。すべてのニュースレターに登録解除リンクがあります。サブスクリプションの管理や解除はこちらから。詳しくはIBMプライバシー・ステートメントをご覧ください。
アプリケーションが、マイクロサービスへのリファクタリングが開始されつつあるほとんどのアプリケーションと同様である場合、そのアプリケーションは単一の大規模なリレーショナルデータベースで動作していると想定してもおそらく問題ありません。さらに、このデータベースがOracle Databaseである可能性はほぼ半々です。残りのすべてのリレーショナルデータベース ( Db2 、 SQL Server 、 Informix 、さらにはMySQLやPostgresなどの OSS データベース) が残りの部分を分割しています。実際、(通常は高価な)エンタープライズ・リレーショナル・データベースから移行することは、マイクロサービスにリファクタリングすることでよく推進されるメリットの1つです。
多くのマイクロサービスでは、NewSQLや NoSQLなど、他のタイプのデータベースを選択する正当な理由があります。ただし、現在のリレーショナルデータベースを一度にすべて放棄することは、多くの場合無謀な決定です。代わりに、既存のJavaコードをリファクタリングするための増分アプローチを推奨しているのと同じように、データベースを変更するためのより増分的なアプローチを検討することもできます。
しかし、私たちが提唱してきたコーディングの増分アプローチと同様に、データベースのリファクタリングの増分アプローチに従う場合の最大の問題は、どこから始めるかを決めることです。増分アプローチを決定したら、最初に決定しなければならないのは、1つの大規模なデータベースを使用するか、それとも多数の小規模なデータベースを使用するかということです。一見すると、これは理にかなっていないように聞こえるかもしれません。言うまでもなく、一つの大きなデータベースなど誰も望んでいません。それこそがモノリスで抱えているものなのですから!まずは私たちが何を意味しているのかを説明しましょう。
基本的に、最初にデータベース サーバーとデータベース スキーマを区別する必要があります。OracleやDb2のようなエンタープライズ規模のデータベースに精通している人にとっては、これは当たり前のことです。なぜなら、企業には通常、1つの大きなOracleサーバー (または、多数の小さなサーバーで構成される大きなサーバーであるRAC) があり、その上で複数のチームが独自のデータベース (それぞれ個別のスキーマで表される) をホストするからです。これが行われる理由は、ライセンスは CPU ごとに付与されることが多く、企業は支払った金額で最大限の利用をしたいと考えているからです。これを実現する方法の 1 つは、複数のチームを 1 つの大きなサーバーにまとめることです。MySQL や Postgre SQL などのオープンソースデータベースに精通している人にとっては、区別する必要が少ないため、この方法はあまり一般的ではありません。
これが重要なのは、マイクロサービスのデータベースの構築については、データベースでの結合を削減または排除することが重要ですが、それは実際にはデータベース・スキーマ・レベルでの結合を意味するからです。問題が発生するのは、同じ情報(同じスキーマ内の同じテーブル)を使用する 2 つの異なるマイクロサービスがある場合です。図1をご覧いただくと、その意味がわかります。
モノリシック・アプリケーション(別名「巨大な泥団子」、つまりすべてが相互に接続されている状態)に相当するデータベースの状態は、すべてのテーブルが相互に接続された、一つの巨大なスキーマを持つことです。こうしたことが発生するたびに、テーブルを分離するために必要な労力は膨大なものになります。
ただし、マイクロサービスに移行する場合、サーバー・レベルでのハードウェアとライセンスの共有によって引き起こされる問題が少なくなることを認識しておく必要があります。実際、マイクロサービスへの最初のリファクタリングを行う際、新しい、より明確に分離されたスキーマを同じエンタープライズ サーバーに保持することには多くの利点があります。企業では通常、データベースのバックアップと復元、およびデータベース サーバーの更新の手順が既に用意されており、チームはそれを活用できるからです。
ある意味では、企業がハードウェアとソフトウェアの提供およびエンタープライズ データベースの管理を通じて提供しているのは、Database-as-a-Service (DBaaS)の限定バージョンです。これは、図 2 に示すように、「モジュラー モノリス」アプローチから始めて、モノリスの各部分を機能領域ごとにより明確に分離することから始めるアプローチに特に適しています。
この例 (進行中のリファクタリング作業を示すためのもの) では、リファクタリングされたアプリケーション内の特定のモジュールに対応する 3 つの新しいスキーマ (A、B、C) に対応するテーブルを分離することによって、データベースがどのように分割されているかを確認できます。このように分離されると、完全に個別のマイクロサービスに分割できます。ただし、D と E はまだリファクタリング中であり、相互接続されたテーブルを持つ単一のスキーマを共有しています。
最終的には、データベース・サーバー・レベルでのリンクさえも問題になる可能性があります。たとえば、エンタープライズ データベースを選択した場合、利用できる機能がエンタープライズ データベースに限定されます。リレーショナル・モデルでも、すべてのスキーマがこれらの機能をすべて必要とするわけではなく、別のサーバーを通じてより適切にサポートされる機能が必要な場合もあります(例:より良いシャーディングが、NewSQLデータベースを使用する理由としてよく挙げられます)。同様に、複数のマイクロサービスで共有されているデータベース・サーバーをアップグレードすると、複数のサービスが一度に停止する可能性があります。
しかし実際は、これは後回しにできる決定であり、プロジェクトの開始時にすぐに決定する必要はありません。チームが最初にリファクタリングを始める際、少なくともリファクタリングプロジェクトの初期段階は同じデータベースサーバーに留めることで、チームがコードやデータベースのリファクタリングに必要な経験を積む間、段階的な変更を加える手段となります。
前のセクションで示唆したように、リファクタリング・プロセスを進めるときは、データベースの選択肢についてもう少し慎重に考える必要があります。すべてのデータ・セットがリレーショナル・モデルに完全に適しているわけではありません。これらのデータセットを管理するための最適なアプローチを決定するために、多くの場合、「実際にデータベースに何を保管しているのか」という質問に帰着します。
私たちは長年にわたり、企業がオブジェクトリレーショナルマッピングフレームワークを構築、維持、そしてしばしば酷使するのを支援してきましたが、実際の問題は、保管されているデータがリレーショナルデータモデルにまったく適切にマッピングされないケースが数多くあるということでした。そのようなことが起こるたびに、リレーショナルモデルを「調整」して適合させるか、あるいは、コードをリレーショナルデータ・ストアに強制的に一致させるためにプログラムでさまざまな困難を乗り越える必要があることに気づきました。
多言語による永続性の選択の時代に入った今、これらの決定のいくつかを再検討し、より良い決定を下すことができます。特に、リレーショナル・モデルが最良の選択肢ではないことが明らかな4つのケースを検討し、次に、リレーショナル・モデルが最良の選択肢であり、データを別の形式に変えることが正しいアプローチではなかったというケースを検討します。
エンタープライズシステムの永続コードを調べてみると、驚いたことに、実際にはリレーショナルデータベースに保管されていたのは、シリアル化された Java オブジェクトのバイナリ表現であることが何度もわかりました。これらは「バイナリ・ラージ・オブジェクト」または「Blob」列に保存され、通常、チームがJavaオブジェクトをリレーショナル・テーブルと列にマップするという複雑さに頭を悩ませた結果です。BLOB ストレージには、列ベースでクエリを実行できない、速度が遅い、Java オブジェクト自体の構造の変更の影響を受けやすい (オブジェクト構造が大幅に変更されると、古いデータが読み取れなくなる可能性がある) などの欠点があります。
もしアプリケーション(あるいは、より可能性が高いのはアプリケーションの一部)がリレーショナルデータベース内でBlobストレージを利用しているなら、それはすでに、MemcachedやRedisのようなキーバリューストアを使用した方が良いかもしれないという、かなり良い兆候です。一方で、一歩引いて、自分が何を保管しているのか少し考察するのも良いでしょう。もしそれが構造化されたJavaオブジェクト(深く構造化されているもののネイティブバイナリではない)であれば、CloudantやMongoDBのようなドキュメント保管を使う方が良いかもしれません。さらに、ドキュメントを保管する方法に少し労力を費やすだけで (たとえば、前述のデータベースは両方とも JSON ドキュメント保管であり、JSON パーサーはどちらも広く利用可能でカスタマイズも簡単です)、ストレージメカニズムがはるかに不透明な Blob 保管アプローチを使用する場合よりも、「スキーマドリフト」の問題をはるかに簡単に処理できます。
数年前、Martin Fowlerが「Patterns of Enterprise Application Architectures」を執筆していたとき、私たちは多くのパターンについて活発なやりとりをし、何度か活発なレビューミーティングを行いました。特に、常に異質なものとして目立っていたのが、Active Record パターンです。これは奇妙なものでした。というのも、Java コミュニティから来た私たちは、このような問題に遭遇したことがなかったからです (Martin は Microsoft .NET プログラミングコミュニティではよくあることだと断言しましたが)。しかし、私たちにとって本当に印象的だったのは、—特にiBatisのようなオープンソース技術を使ったいくつかのJava実装を見始めたとき—、それを使うのに最適なケースは、そのオブジェクトが、まさしくフラットな場合であるように思えた、ということでした。
データベースにマッピングするオブジェクトが完全かつ完全にフラットである場合(ネストされたオブジェクトなどの限定的な例外を除き)、他のオブジェクトとの関係がまったくない場合、リレーショナル・モデルの全機能を活用していない可能性があります。実際、ドキュメントを保管する可能性の方がはるかに高いです。このようなケースでは、顧客満足度調査や問題チケットなど、チームが文字通り電子版の紙文書を保管しているケースがよくあります。そのような状況では、Cloudant や MongoDB のようなドキュメントデータベースがおそらく最適でしょう。コードをそのタイプのデータベースで機能する独自のサービスに分割すると、大規模なエンタープライズデータベースの一部として同じことを行うよりも、成果がはるかに単純になり、多くの場合、保守も簡単になります。
ORMシステムでよく見られるもう1つのパターンは、「メモリ内のキャッシュに取り込まれたテーブル内の参照データ」の組み合わせです。参照データは、頻繁に(またはまったく)更新されないが、常に読み取られるものから構成されます。良い例としては、米国の州やカナダの州のリストが挙げられます。その他の例としては、医療コードや標準部品リストなどがあります。この種のデータは、GUIのドロップダウンへの入力によく使用されます。
一般的なパターンは、使用するたびにテーブル(通常はフラット2列、最大3列のテーブル)からリストを読み取ることから始めます。ただし、毎回それを実行するパフォーマンスは法外であることがわかり、代わりに、アプリケーションは起動時にEhcacheなどのメモリ内キャッシュにそれを読み取ります。
この問題が発生するたびに、よりシンプルで高速なキャッシュ・メカニズムにリファクタリングしようとします。繰り返しますが、これは Memcached または Redis が最適である状況です。参照データがデータベース構造の他の部分から独立している場合(多くの場合独立しているか、せいぜい疎結合である場合)、そのデータと関連サービスをシステムから分離することが有効です。
私たちが取り組んでいたある顧客システムでは、プログラムが操作するオブジェクトを作成するためだけに、非常に複雑なクエリ (6 または 7 方向の結合程度) を必要とする複雑な財務モデリングを実行していました。更新はさらに複雑で、何が変更されたか、データベース内の内容が作成して操作した構造とまだ一致しているかどうかを確認するためだけに、いくつかの異なるレベルの楽観的ロックチェックを組み合わせる必要がありました。
振り返ってみると、私たちが行っていたことは、グラフとしてモデル化した方がより自然だったことは明らかです。このような状況(具体的には、異なる種類の株式や債務で構成され、それぞれ異なる通貨建てで異なる満期を持ち、評価ルールも異なる資金のトランシェをモデル化していた)では、本当にやりたいこと—グラフを上下に移動し、グラフの一部を自由に移動させる——容易に実現できるデータ構造がほぼ必須となります。
このような場合は、Apache TinkerpopやNeo4Jのようなソリューションが適しています。ソリューションをグラフとして直接モデル化することで、多くの複雑な Java および SQL コードを回避でき、同時にランタイムのパフォーマンスが大幅に向上したと考えられます。
特定のデータ構造に対してNoSQLデータベースが論理的に適切なアプローチとなるケースは数多く存在するものの、リレーショナルモデルの柔軟性と強力さに勝るものは往々にして見当たりません。リレーショナル・データベースの優れた点は、同じデータをさまざまな目的のためにさまざまな形式に「スライス・アンド・ダイス」できることです。データベースビューなどのトリックを使用すると、同じデータの複数のマッピングを作成できます。これは、複雑なデータ モデルの関連するクエリのセットを実装するときに役立ちます。
NoSQLとSQLの比較について詳しく知りたい方は、「SQLとNoSQLデータベースの相違とは」をご覧ください。
前回の記事で説明したように、マイクロサービスの「下限」が、そのデータに対して動作する一連の関連サービスとともに集約にグループ化された一連のエンティティである場合、一連の関連クエリを表すビューを実装すると、多くの場合、SQL を使用することで最も簡単でわかりやすくなります。
前の記事の単純なアカウント/明細項目の例につながった顧客の例で初めて確認しました。この場合、ある銀行は、ドキュメント指向の NoSQL データベースでこれとまったく同じ単純なモデルを動作させようと懸命に努力しましたが、 CAP 定理によって敗北しました。しかし、チームがそのデータモデルを選択したのはまったく間違った理由からでした。同社は、アーキテクチャーの一貫性に対する不適切な要望から、多様なマイクロサービスすべてにドキュメント指向データベースを使用することを選択しました。
この場合、ACIDモードのすべての属性が必要でしたが、シャーディング(パーティショニングなど)は必要ありませんでした。既存のシステムは、リレーショナル・モデル上で完全に許容できるレベルの性能で長年にわたって機能しており、大きな成長が見込まれていたわけではありませんでした。しかし、システムの中核には ACID トランザクションが必要で、パーティショニングは必要ありませんでしたが、必ずしもシステムのさまざまな部分すべてがそうであるとは限りませんでした。SQLデータベースが得意とすることがいくつかあり、ACIDトランザクションもそのひとつです。マイクロサービス・システムでは、通常、ACIDトランザクションを操作する最小のデータ・セットを中心にグループ化することが適切なアプローチです。
SQLは必ずしも従来のSQLデータベースを指すわけではありません。確かに、多くのマイクロサービスアーキテクチャではそれが可能なのですが、SQL は少なくとも他の 2 種類のデータベースにも実装されており、マイクロサービスを実装する多くのチームにとって便利な選択肢となり得ます。1つ目は「小さなSQL」で、MySQLやPostgresなどのオープンソース・データベースのドメインです。マイクロサービスを実装する多くのチームにとって、これらのデータベースは次のような多くの理由から完全に合理的な選択です。
これらの「小規模SQL」データベースを使用することの主な欠点は、エンタープライズ・データベースがサポートできるのと同じレベルのスケール(特にシャーディングに関して)をサポートしていないことが多い点です。しかし、小規模な SQL Database の長所と NoSQL データベースの拡張性を組み合わせた、新しいデータベースベンダーやオープンソースプロジェクトによって、この問題は見事に解決されています。「NewSQL」データベースとも呼ばれるこれらには、CockroachDB、Apache Trofidion、Clustrix などがあります。
すべてのACIDトランザクション、リレーショナルモデルを完全にサポートするSQLエンジン、そして幅広いスケーリングとシャーディング機能が必要なときはいつでも、NewSQLデータベースはチームにとって良い選択肢となります。ただし、その選択には代償が伴います。これらのデータベースは、多くの場合、古い「スモールSQL」ソリューションよりもセットアップと管理が複雑です。また、このようなデータベースのスキルを持つ人を見つけることも困難です。いずれにしても、選択肢を検討する際には、これらを慎重に考慮する必要があります。
私たちは、コーディングの問題として現れる可能性のある、さまざまなデータベースモデリングの問題を徹底的に調べました。こうした問題が複数ある場合は、既存のエンタープライズ・データ・ストアから分離し、別のタイプのデータ・ストアで再構築することをお勧めします。いずれにせよ、これをゆっくりと段階的に実行することをお勧めします。すべてのアプリケーションにあらゆる種類のデータ・モデルが必要なわけではないこと、および、大規模な実装を開始する前に、スキルを構築し、選択したデータベースの運用機能を時間をかけて理解する必要があることを注意してください。
最後に、マイクロサービスアプローチ全体は、各マイクロサービスを構築するチームが自分たちにとってどのデータ・ストアが適切であるかを独自に決定できるという考えに基づいているという事実を認識してください。私たちが直面した最大の問題は(これらのストーリーの多くが示唆しているように)、単一のデータ表現とストレージ・アプローチをあらゆる状況に対応させようとしているものです。
NoSQLデータベースを使用すべきでなかった状況で、チームがCAP定理の現実に対処しなければならないという状況を何度も見てきました。同様に、NoSQL データベースから複雑なレポートを作成しようとすると、イライラする結果になることがよくあります。一方、ここで示した状況は、リレーショナルモデルがすべてを網羅するものではないことを示しています。他のモデルにも、同様に適切な場所があります。私たちが提供できる最良のアドバイスの1つは、各マイクロサービスに適したモデルを選択するために必要な自主性をチームが確保することです。
IBM Garageは、迅速に行動し、スマートに取り組み、破壊的な変化をさらに破壊できるようなイノベーションのために構築されています。
Red Hat OpenShift on IBM Cloudは、フルマネージド型のOpenShift Container Platform(OCP)です。
DevOpsソフトウェアとツールを使用して、複数のデバイスと環境にわたって、クラウドネイティブなアプリを構築、デプロイ、管理します。
IBMのクラウド・コンサルティング・サービスで新しい機能にアクセスし、ビジネスの俊敏性を高めましょう。ハイブリッドクラウド戦略や専門家とのパートナーシップを通じて、ソリューションを共創し、デジタル・トランスフォーメーションを加速させ、パフォーマンスを最適化する方法をご覧ください。