マイクロサービスでのクリーンなアーキテクチャの実装

こんにちは、Habr!



現在、多くのプロジェクトでマイクロサービスアーキテクチャが使用されています。私たちも例外ではなく、2年以上の間、マイクロサービスを使用して銀行の法人向けのRBSを構築しようとしています。







記事の著者: ctimas そして Alexey_Salaev



マイクロサービスアーキテクチャの重要性



私たちのプロジェクトは法人向けのRBSです。内部の多くの異なるプロセスと素晴らしいミニマルなインターフェース。しかし、必ずしもそうではありませんでした。長い間、請負業者からのソリューションを使用していましたが、ある晴れた日、私たちの製品を開発することが決定されました。



プロジェクトを始めて、多くの議論がありました:どのアプローチを選ぶべきですか?新しいRBSシステムを構築する方法は?それはすべて「モノリスvsマイクロサービス」の議論から始まりました:使用される可能性のあるプログラミング言語について議論し、フレームワークについて議論しました(「スプリングクラウドを使用する必要がありますか?」、「マイクロサービス間の通信にどのプロトコルを選択する必要がありますか?」)。これらの質問には、原則として回答数が限られており、ニーズと機能に応じて特定のアプローチとテクノロジーを選択するだけです。そして、「マイクロサービスを自分で作成する方法」という質問に対する答えです。完全に単純ではありませんでした。



多くの人がこう言うかもしれません。「なぜマイクロサービス自体の一般的なアーキテクチャの概念を開発するのですか?エンタープライズアーキテクチャとプロジェクトアーキテクチャ、そして開発の一般的なベクトルがあります。チームにタスクを割り当てると、チームはタスクを完了し、マイクロサービスが作成されてタスクを実行します。結局のところ、これがマイクロサービスの本質であり、独立性です。」そして、彼らは絶対に正しいでしょう!しかし、時間の経過とともにチームが大きくなるため、マイクロサービスと従業員の数が増え、昔の人は少なくなります。プロジェクトに没頭する必要のある新しい開発者がやって来ます。一部の開発者はチームを変更します。また、チームは時間の経過とともに存在しなくなりますが、マイクロサービスは存続し、場合によっては改善が必要になります。



マイクロサービスアーキテクチャの一般的な概念を開発している間、私たちは将来のために大きな余裕を残しています。



  • ;
  • ;
  • : .




マイクロサービスを使用するすべての人は、長所と短所をよく知っています。その1つは、古い実装を新しい実装にすばやく置き換える機能です。しかし、マイクロサービスを簡単に交換できるようにするには、マイクロサービスをどれだけ小さくする必要がありますか?マイクロサービスのサイズを決定する境界はどこにありますか?ミニモノリスやナンサービスを作らない方法は?そして、ロジックのごく一部を実行し、そのような関数を呼び出す順序を構築することでビジネスプロセスを構築する関数の側にいつでも直接行くことができます...



ビジネスドメインごとにマイクロサービスを選び出すことにしました(たとえば、ルーブル支払い)、このドメインのタスクに従ってマイクロサービス自体を構築します。



銀行の標準的なビジネスプロセスの例を考えてみましょう-「支払い注文の作成」







一見単純なクライアント要求は、かなり大きな一連の操作であることがわかります。このシナリオは概算であり、簡単にするために一部の段階は省略されています。一部の段階はインフラストラクチャコンポーネントのレベルで発生し、製品サービスの主要なビジネスロジックに到達せず、操作の他の部分は非同期で機能します。要するに、ある時点で多くの隣接するサービスを使用し、さまざまなライブラリの機能を使用し、それ自体の中にある種のロジックを実装し、さまざまなストレージにデータを保存できるプロセスがあるということです。



よく見ると、ビジネスプロセスは非常に直線的であり、その作業の過程で、どこかでデータを取得するか、何らかの方法でデータを処理する必要があります。これには、外部データソースの操作が必要になる場合があります(マイクロサービス、データベース)またはロジック(ライブラリ)。







一部のマイクロサービスはこの概念に適合しませんが、全体の割合に占めるそのようなマイクロサービスの数は少なく、約5%になります。



クリーンなアーキテクチャ



コードを整理するためのさまざまなアプローチを検討した後、マイクロサービス内のコードをレイヤーとして整理することにより、「クリーンアーキテクチャ」アプローチを試すことにしました。



「クリーンアーキテクチャ」自体に関しては、複数の本が書かれており、インターネットとハブレの両方に多くの記事があり(記事1記事2)、その長所と短所が何 度も議論されています。



このトピックで見つけることができる人気のある図は、ボブ・マーチンの著書「クリーンアーキテクチャ」で描かれています。







ここでは、中央の左側の円グラフがレイヤー間の依存関係の方向を示し、右隅に適度に表示されます。実行の流れの方向。



このアプローチは、実際、他のプログラミングテクノロジーと同様に、長所と短所があります。しかし、私たちにとって、このアプローチを使用する場合、否定的な側面よりもはるかに肯定的な側面があります。



プロジェクトでの「クリーンアーキテクチャ」の実装



シナリオに基づいてこの図を再描画しました。







当然、この図は1つのシナリオを反映しています。マイクロサービスが1つのドメインエンティティでより多くの操作を実行することがよくありますが、公平を期すために、多くのアダプターを再利用できます。



マイクロサービスをレイヤーに分割するためにさまざまなアプローチを使用できますが、プロジェクトビルダーのレベルでモジュールに分割することを選択しました。モジュールレベルでの実装は、プロジェクトの視覚的な認識を容易にし、建築様式の誤用に対するプロジェクトの保護の別の層も提供します。



経験から、プロジェクトに没頭するとき、新しい開発者は理論的な部分に精通する必要があり、ほとんどすべてのマイクロサービスを簡単かつ迅速にナビゲートできることに気付きました。



Gradleを使用してJavaでマイクロサービスを構築するため、メインレイヤーはモジュールのセットとして形成されます。







現在、私たちのプロジェクトは、コントラクトを実装するか、それらを使用するモジュールで構成されています。これらのモジュールが機能し、問題の解決を開始するには、依存性注入を実装し、アプリケーション全体を起動するエントリポイントを作成する必要があります。そして、ここにボブおじさんの「純粋なアーキテクチャ」という本に興味深い質問があります。詳細、モデル、フレームワークについて説明する章全体がありますが、フレームワークやデータベースを中心にアーキテクチャを構築するのではなく、次のように使用します。コンポーネントの1つ...



エンティティを保存する必要がある場合、データベースを参照します。たとえば、スクリプトが実行時に必要なコントラクト実装を受け取るために、次のフレームワークを使用します。アーキテクチャDI。



データベースなしでマイクロサービスを実装する必要がある場合、またはDIを放棄できる場合のタスクがあります。これは、タスクが単純すぎて、正面から解決する方が速いためです。また、「リポジトリ」モジュールでデータベースを使用してすべての作業を実行する場合、フレームワークを使用してすべてのDIを準備するのはどこでしょうか。それほど多くのオプションはありません。アプリケーションの各モジュールに依存関係を追加するか、DI全体を個別のモジュールとして選択しようとします。

別の新しいモジュールアプローチを選択し、それを「インフラストラクチャ」または「アプリケーション」と呼びます。



確かに、そのようなモジュールが導入されると、原則にわずかに違反します。これによれば、すべての依存関係をドメインレイヤーの中心に向けます。アプリケーション内のすべてのクラスにアクセスできる必要があります。



タマネギにインフラストラクチャレイヤーをレイヤーの形で追加することはできません。そこにその場所はありませんが、ここではすべてを別の角度から見ることができ、円があります。インフラストラクチャ」と私たちのパフオニオンはその上にあります...わかりやすくするために、レイヤーを少し離して見やすくしてみましょう。







新しいモジュールを追加し、インフラストラクチャレイヤーの依存関係のツリーを見て、モジュール間の最終的な依存関係を確認します。 あと







は、 DIフレームワーク自体。プロジェクトではSpringを使用していますが、これは必須ではありません。DIを実装する任意のフレームワーク(たとえば、micronaut)を使用できます。



マイクロサービスを構築する方法とコードのどの部分を配置するかはすでに決定しています。ビジネスシナリオをもう一度検討する価値があります。もう一つ興味深い点があります。







この図は、アクションの権利のチェックがメインスクリプトで実行されない可能性があることを示しています。これは、次に何が起こるかに依存しない別個のタスクです。署名の検証を別のマイクロサービスに移動することもできますが、ここではマイクロサービスの境界を定義するときに多くの矛盾があり、アーキテクチャに別のレイヤーを追加することにしました。



別々のレイヤーで、署名の検証など、アプリケーションで繰り返すことができる段階を強調する必要があります。この手順は、ドキュメントを作成、変更、または署名するときに発生する可能性があります。多くのメインスクリプトは、最初に小さな操作を開始し、次にメインスクリプトのみを開始します。したがって、小さな操作を小さなスクリプトに分離し、レイヤーに分割して、より便利に再利用できるようにする方が簡単です。



このアプローチにより、ビジネスロジックが理解しやすくなり、時間の経過とともに、再利用可能なスモールビジネスのビルディングブロックのセットが形成されます。



アダプター、コントローラー、リポジトリーのコードについて言うことはあまりありません。それらは非常に単純です。別のマイクロサービスのアダプターは、Swagger、Spring RestTemplate、またはGrpcクライアントから生成されたクライアントを使用します。リポジトリ内-Hibernateまたは他のORMを使用するバリエーションの1つ。コントローラは、使用するライブラリに従います。



結論



この記事では、マイクロサービスアーキテクチャを構築する理由、使用するアプローチ、および開発方法を示したいと思いました。私たちのプロジェクトは若く、その旅の始まりに過ぎませんが、マイクロサービス自体のアーキテクチャの観点から、開発の主なポイントを強調することができます。



マルチモジュールマイクロサービスを構築しており、次のような利点があります。



  • , - , , - , ;
  • , , - ;
  • , Api, , , , ;
  • , , , , .


もちろん、軟膏のハエなしではありません。たとえば、最も明白なことは、多くの場合、各モジュールが独自の小さなモデルで動作することです。たとえば、コントローラーには残りのモデルの説明があり、リポジトリにはデータベースエンティティがあります。これに関連して、オブジェクトを相互にマッピングする必要がありますが、「mapstruct」などのツールを使用すると、これを迅速かつ確実に行うことができます。



また、デメリットには、他の開発者を常に監視する必要があるという事実が含まれます。これは、コストよりも少ない作業を実行したいという誘惑があるためです。たとえば、フレームワークを1つのモジュールより少し遠くに移動しますが、これはアーキテクチャ全体でのこのフレームワークの責任の低下につながり、将来的には改善の速度に悪影響を与える可能性があります。



マイクロサービスを実装するためのこのアプローチは、寿命の長いプロジェクトや複雑な動作のプロジェクトに適しています。インフラストラクチャ全体の実装には時間がかかるため、将来的には安定性と迅速な改善で成果を上げます。



All Articles