Raiffeisenbank .NET 開発者コミュニティは、ViennaNET の内容を簡単にレビューし続けています。 私たちがこれに至った経緯と理由については、
この記事では、分散トランザクション、キュー、データベースを操作するためのまだ検討されていないライブラリについて説明します。これらのライブラリは GitHub リポジトリ (
ウィーンネットサガス
プロジェクトが DDD およびマイクロサービス アーキテクチャに切り替わり、ビジネス ロジックがさまざまなサービスに分散されると、多くのシナリオが一度に複数のドメインに影響を与えることが多いため、分散トランザクション メカニズムの実装の必要性に関連する問題が発生します。 このようなメカニズムについてさらに詳しく知ることができます。たとえば、
私たちのプロジェクトでは、シンプルだが便利なメカニズム、つまりオーケストレーションベースの物語を実装しました。 その本質は次のとおりです。さまざまなサービスで操作を順番に実行する必要がある特定のビジネス シナリオがあり、いずれかのステップで問題が発生した場合は、前のすべてのステップのロールバック プロシージャを呼び出す必要があります。提供された。 したがって、物語の終わりには、成功に関係なく、すべてのドメインで一貫したデータを受け取ります。
私たちの実装はまだ基本的な形式で作成されており、他のサービスと対話する方法の使用には関連付けられていません。 使い方は難しくありません。基本抽象クラス SagaBase<T> の子孫を作成するだけです。T は、サガが動作するために必要な初期データといくつかの中間結果を格納できるコンテキスト クラスです。 コンテキスト インスタンスは、実行中にすべてのステップに渡されます。 Saga 自体はステートレス クラスであるため、インスタンスをシングルトンとして DI に配置して、必要な依存関係を取得できます。
広告の例:
public class ExampleSaga : SagaBase<ExampleContext>
{
public ExampleSaga()
{
Step("Step 1")
.WithAction(c => ...)
.WithCompensation(c => ...);
AsyncStep("Step 2")
.WithAction(async c => ...);
}
}
呼び出しの例:
var saga = new ExampleSaga();
var context = new ExampleContext();
await saga.Execute(context);
さまざまな実装の完全な例を表示できます
ViennaNET.Orm.*
Nhibernate を介してさまざまなデータベースを操作するためのライブラリのセット。 Liquibase を使用した DB-First アプローチを使用しているため、既製のデータベース内のデータを操作する機能のみがあります。
ViennaNET.Orm.Seedwork и ViennaNET.Orm
– 基本的なインターフェイスとその実装をそれぞれ含むメイン アセンブリ。 それらの内容をさらに詳しく見てみましょう。
インターフェース IEntityFactoryService
とその実装 EntityFactoryService
作業単位、特定のエンティティを操作するためのリポジトリ、およびコマンドの実行プログラムや直接 SQL クエリがここで作成されるため、データベースを操作するための主な開始点になります。 場合によっては、データベースを操作するクラスの機能を制限すると便利な場合があります (たとえば、データの読み取りのみの機能を提供するなど)。 そのような場合に備えて IEntityFactoryService
祖先 - インターフェイスがあります IEntityRepositoryFactory
、リポジトリを作成するメソッドのみを宣言します。
データベースに直接アクセスするには、プロバイダー メカニズムが使用されます。 私たちのチームで使用する各 DBMS には独自の実装があります。 ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql
.
同時に、複数のプロバイダーを XNUMX つのアプリケーションに同時に登録できるため、たとえば、インフラストラクチャを変更するコストをかけずに、XNUMX つのサービスのフレームワーク内で、次のサービスから段階的な移行を実行できます。ある DBMS から別の DBMS へ。 必要な接続、つまり特定のエンティティ クラス (データベース テーブルへのマッピングが書き込まれる) のプロバイダーを選択するメカニズムは、エンティティを BoundedContext クラス (ドメイン エンティティを登録するメソッドを含む) またはその後継クラスに登録することで実装されます。 ApplicationContext (アプリケーション エンティティ、直接リクエスト、およびコマンドを登録するためのメソッドが含まれます)。構成からの接続識別子が引数として受け入れられます。
"db": [
{
"nick": "mssql_connection",
"dbServerType": "MSSQL",
"ConnectionString": "...",
"useCallContext": true
},
{
"nick": "oracle_connection",
"dbServerType": "Oracle",
"ConnectionString": "..."
}
],
アプリケーションコンテキストの例:
internal sealed class DbContext : ApplicationContext
{
public DbContext()
{
AddEntity<SomeEntity>("mssql_connection");
AddEntity<MigratedSomeEntity>("oracle_connection");
AddEntity<AnotherEntity>("oracle_connection");
}
}
接続 ID が指定されていない場合は、「default」という名前の接続が使用されます。
エンティティのデータベース テーブルへの直接マッピングは、標準の NHibernate ツールを使用して実装されます。 この説明は、xml ファイルとクラスの両方を通じて使用できます。 単体テストでスタブ リポジトリを簡単に作成できるように、ライブラリがあります。 ViennaNET.TestUtils.Orm
.
ViennaNET.Orm.* の完全な使用例を見つけることができます。
ViennaNET.Messaging.*
キューを操作するためのライブラリのセット。
キューを操作するには、さまざまな DBMS と同じアプローチ、つまり、使用するキュー マネージャーに関係なく、ライブラリの操作に関して可能な限り最大限の統一アプローチが選択されました。 図書館 ViennaNET.Messaging
まさにこの統一に責任を負っており、 ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue
それぞれ、IBM MQ、RabbitMQ、Kafka のアダプター実装が含まれています。
キューを操作する場合、メッセージの受信と送信という XNUMX つのプロセスが存在します。
受け取ることを検討してください。 ここには 2 つのオプションがあります。常時リッスンする場合と、単一のメッセージを受信する場合です。 キューを常にリッスンするには、最初にキューから継承されたプロセッサ クラスを記述する必要があります。 IMessageProcessor
、受信メッセージの処理を担当します。 次に、特定のキューに「リンク」する必要があります。これは、への登録を通じて行われます。 IQueueReactorFactory
構成からのキュー識別子を示します。
"messaging": {
"ApplicationName": "MyApplication"
},
"rabbitmq": {
"queues": [
{
"id": "myQueue",
"queuename": "lalala",
...
}
]
},
リスニングを開始する例:
_queueReactorFactory.Register<MyMessageProcessor>("myQueue");
var queueReactor = queueReactorFactory.CreateQueueReactor("myQueue");
queueReactor.StartProcessing();
次に、サービスが開始され、メソッドが呼び出されてリスニングを開始すると、指定されたキューからのすべてのメッセージが対応するプロセッサに送られます。
ファクトリインターフェイスで単一のメッセージを受信するには IMessagingComponentFactory
方法があります CreateMessageReceiver
これにより、指定されたキューからのメッセージを待機する受信者が作成されます。
using (var receiver = _messagingComponentFactory.CreateMessageReceiver<TestMessage>("myQueue"))
{
var message = receiver.Receive();
}
メッセージを送信するには 同じものを使用する必要があります IMessagingComponentFactory
そしてメッセージ送信者を作成します。
using (var sender = _messagingComponentFactory.CreateMessageSender<MyMessage>("myQueue"))
{
sender.SendMessage(new MyMessage { Value = ...});
}
メッセージのシリアル化と逆シリアル化には、テキスト、XML、JSON の XNUMX つの既製のオプションがありますが、必要に応じて独自のインターフェイス実装を簡単に作成できます。 IMessageSerializer и IMessageDeserializer
.
私たちは、各キューマネージャーの固有の機能を保持するよう努めてきました。 ViennaNET.Messaging.MQSeriesQueue
テキストだけでなくバイトメッセージも送信できます。 ViennaNET.Messaging.RabbitMQQueue
ルーティングとオンザフライキューイングをサポートします。 RabbitMQ のアダプター ラッパーも、RPC の類似点を実装しています。つまり、メッセージを送信し、XNUMX つの応答メッセージに対してのみ作成される特別な一時キューからの応答を待ちます。
ViennaNET.CallContext
キューは、異なるシステム間の統合だけでなく、たとえば、サガ内の同じアプリケーションのマイクロサービス間の通信にも使用されます。 このため、ユーザー ログイン、エンドツーエンド ロギング用の要求識別子、送信元 IP アドレス、認証データなどの補助データをメッセージと一緒に送信する必要がありました。 このデータの転送を実装するために、ライブラリを開発しました。 ViennaNET.CallContext
これにより、サービスに入るリクエストからのデータを保存できます。 この場合、リクエストがキューを通じて行われたか、HTTP 経由で行われたかは関係ありません。 次に、送信リクエストまたはメッセージを送信する前に、データがコンテキストから取得され、ヘッダーに配置されます。 したがって、次のサービスは補助データを受信し、同じ方法で管理します。
ご清聴ありがとうございます。コメントやプルリクエストをお待ちしております。
出所: habr.com