ViennaNET:バックエンド用のライブラリのセット。パート2

Raiffeisenbank .NET開発コミュニティは、ViennaNETの短い内訳を続けています。最初の部分で、私たちがこれ到達した方法と理由について読むことができます



この記事では、分散トランザクション、キュー、およびデータベースを操作するための、まだ検討されていないライブラリについて説明します。これらは、GitHubのリポジトリ(ソースはここにあります)にあり、Nugetパッケージはここにあります







ViennaNET.Sagas



プロジェクトがDDDおよびマイクロサービスアーキテクチャに移行する場合、ビジネスロジックがさまざまなサービスに分散すると、多くのシナリオが一度に複数のドメインに影響を与えることが多いため、分散トランザクションのメカニズムを実装する必要性に関連する問題が発生します。このようなメカニズムの詳細については、たとえば、ChrisRichardsonによる「MicroservicesPatterns」という本を参照してください。



私たちのプロジェクトでは、シンプルでありながら便利なメカニズムを実装しました。それは、サガ、つまりオーケストレーションに基づくサガです。その本質は次のとおりです。異なるサービスで操作を順次実行する必要がある特定のビジネスシナリオがありますが、いずれかのステップで問題が発生した場合は、提供されている前のすべてのステップのロールバック手順を呼び出す必要があります。したがって、サガの終わりには、成功に関係なく、すべてのドメインにわたって一貫したデータが得られます。



私たちの実装はまだ基本的であり、他のサービスとの相互作用の方法の使用に結び付けられていません。使用するのは難しくありません。基本抽象クラスSagaBase <T>から継承するだけで十分です。ここで、Tはコンテキストクラスであり、Sagaが機能するために必要な初期データといくつかの中間結果を格納できます。コンテキストインスタンスは、実行時にすべてのステップに転送されます。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-ベースインターフェイスとその実装をそれぞれ含むメインアセンブリ。それらの内容についてさらに詳しく見ていきましょう。作業単位、特定のエンティティを操作するためのリポジトリ、コマンドの実行者、および直接SQLクエリがここで作成されるため、



インターフェイスIEntityFactoryServiceとその実装EntityFactoryServiceはデータベースを操作するための主要な開始点です。たとえば、読み取り専用データを有効にするために、データベースを操作するためのクラスの機能を制限すると便利な場合があります。そのような場合、それIEntityFactoryServiceは祖先を持っています-IEntityRepositoryFactoryリポジトリを作成するためのメソッドだけが宣言されているインターフェースです。



データベースに直接アクセスするには、プロバイダーメカニズムが使用されます。データベースチームで使用されるそれぞれについて、独自の実装がありますViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql



同時に、複数のプロバイダーを1つのアプリケーションに同時に登録できます。これにより、たとえば、1つのサービスのフレームワーク内で、インフラストラクチャの更新に費用をかけることなく、1つのDBMSから別のDBMSに段階的に移行できます。必要な接続を選択するメカニズム、したがって特定のエンティティクラス(データベーステーブルへのマッピングが書き込まれる)のプロバイダーは、BoundedContextクラス(ドメインエンティティを登録するためのメソッドを含む)またはその後継のApplicationContext(アプリケーションエンティティを登録するためのメソッドを含む)にエンティティを登録することで実装されます。 、直接要求およびコマンド)、構成からの接続IDが引数として使用されます。



"db": [
  {
    "nick": "mssql_connection",
    "dbServerType": "MSSQL",
    "ConnectionString": "...",
    "useCallContext": true
  },
  {
    "nick": "oracle_connection",
    "dbServerType": "Oracle",
    "ConnectionString": "..."
  }
],


ApplicationContextの例:



internal sealed class DbContext : ApplicationContext
{
  public DbContext()
  {
    AddEntity<SomeEntity>("mssql_connection");
    AddEntity<MigratedSomeEntity>("oracle_connection");
    AddEntity<AnotherEntity>("oracle_connection");
  }
}


接続識別子が指定されていない場合、「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用のアダプターの実装が含まれています。



キューの操作には、メッセージの受信と送信の2つのプロセスがあります。



取得することを検討してください。ここには2つのオプションがあります。常に聞くためと単一のメッセージを受信するためです。常にキューをリッスンするには、最初に、から継承されたプロセッサクラスを記述する必要がありますIMessageProcessor着信メッセージの処理を担当します。さらに、特定のキューに「結び付ける」必要があります。これはIQueueReactorFactory、構成からキューIDを指定して登録することによって行われます。



"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の3つの既製のオプションがありますが、必要に応じて、インターフェイスの独自の実装を安全に作成できますIMessageSerializer IMessageDeserializer



たとえば、ViennaNET.Messaging.MQSeriesQueueテキストメッセージだけでなくバイトメッセージも送信でき、ViennaNET.Messaging.RabbitMQQueueその場でルーティングとキューイングサポートするなど、各キューマネージャーの固有の機能を維持しようとしました。RabbitMQのアダプターラッパーは、RPCのいくつかの類似点も実装します。メッセージを送信し、1つの応答メッセージに対してのみ作成される特別な一時キューからの応答を待ちます。



これは、基本的な接続のニュアンスを持つキューの使用例です



ViennaNET.CallContext



キューは、異なるシステム間の統合だけでなく、たとえば、サガのフレームワーク内での1つのアプリケーションのマイクロサービス間の通信にも使用されます。これにより、ユーザー名、エンドツーエンドロギングのリクエストID、送信元IPアドレス、認証データなどの補助データをメッセージとともに転送する必要が生じました。このデータの転送を実装するためViennaNET.CallContextに、サービスに入る要求からのデータを格納できるライブラリが開発されました。この場合、キューまたはHttpを介してリクエストがどのように行われたかは重要ではありません。次に、送信要求またはメッセージを送信する前に、データがコンテキストから取り出され、ヘッダーに配置されます。したがって、次のサービスは補助データを受け取り、同じ方法でそれらを破棄します。



ご清聴ありがとうございました。コメントやプルリクエストをお待ちしております!



All Articles