🛠️

Avanade Beef で変更通知を Azure Service Bus に送信する

2023/04/20に公開

はじめに

Avanade Beef (以下、Beef) は ASP.NET Core をベースとする Web API の自動生成ツールです。

https://github.com/Avanade/Beef

概要については以下のスライドもご覧ください。

Beef ではイベント駆動型のアーキテクチャを採用しています。これにより外部のシステムとの連携や非同期でのジョブの実行を実現することができます。ただ例によってあまり情報がないため、順を追って実装方法を説明していきたいと思います。

サンプル コード

https://github.com/karamem0/samples/tree/main/avanade-beef-event-sender

実行手順

プロジェクトの作成

v5 からデータ ソースの指定の方法が変わりました。Entity Framework を使用する場合は SqlServer です。

dotnet new beef --company Karamem0 --appname SampleApplication --datasource SqlServer

Business プロジェクトに CoreEx.Azure を追加します。

dotnet add package CoreEx.Azure

エラーが出る場合はそのほかの CoreEx のライブラリとバージョン (>=2.7.0) を合わせてください。

テーブル定義

Product テーブルを作成します。

CREATE TABLE [SampleApplication].[Product] (
  [ProductId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY,
  [ProductName] NVARCHAR (255) NOT NULL,
  [Price] DECIMAL (15, 0) NOT NULL,
  [CreatedBy] NVARCHAR(250) NULL,
  [CreatedDate] DATETIME2 NULL,
  [UpdatedBy] NVARCHAR(250) NULL,
  [UpdatedDate] DATETIME2 NULL
);

コードの修正

Database/database.beef-5.yaml

Entity Framework のモデルを定義します。

schema: SampleApplication
tables:
  - name: Product
    efModel: true

コードを自動生成します。

dotnet run all

CodeGen/entity.beef-5.yaml

エンティティを定義します。

eventSubjectRoot: Karamem0
eventActionFormat: PastTense
eventSourceRoot: Karamem0/SampleApplication
eventSourceKind: Relative
eventPublish: DataSvc
webApiAutoLocation: true
refDataText: true
entities:
  - name: Product
    text: Product
    collection: true
    collectionResult: true
    get: true
    getAll: true
    create: true
    update: true
    delete: true
    validator: ProductValidator
    webApiRoutePrefix: products
    autoImplement: EntityFramework
    entityFrameworkModel: EfModel.Product
    properties:
      - name: ProductId
        type: Guid
        primaryKey: true
      - name: ProductName
        type: string
      - name: Price
        type: decimal
      - name: ChangeLog
        type: ChangeLog

コードを自動生成します。

dotnet run all

Business/DataSvc/Generated/ProductDataSvc.cs

自動生成されたコードは以下のようになっています。

/// <summary>
/// Creates a new <see cref="Product"/>.
/// </summary>
/// <param name="value">The <see cref="Product"/>.</param>
/// <returns>The created <see cref="Product"/>.</returns>
public Task<Product> CreateAsync(Product value) => DataSvcInvoker.Current.InvokeAsync(this, async _ =>
{
    var __result = await _data.CreateAsync(value ?? throw new ArgumentNullException(nameof(value))).ConfigureAwait(false);
    _events.PublishValueEvent(__result, new Uri($"karamem0/sampleapplication/product/{__result.ProductId}", UriKind.Relative), $"Karamem0.SampleApplication.Product", "Created");
    return _cache.SetValue(__result);
}, new InvokerArgs { EventPublisher = _events });

この PublishValueEvent メソッドが Azure Service Bus へのイベントを発火するところになります。

Api/Startup.cs

Azure Service Bus の Client および Sender を追加します。

  // Add the event publishing; this will need to be updated from the logger publisher to the actual as appropriate.
  services.AddEventDataFormatter()
-         .AddLoggerEventPublisher();
+         .AddEventDataSerializer()
+         .AddEventPublisher()
+         .AddAzureServiceBusSender()
+         .AddAzureServiceBusClient();

Api/appsettings.json

Azure Service Bus への接続文字列とキュー/トピック名を指定します。

  {
    ...
    "ConnectionStrings": {
      "Database": "Data Source=.;Initial Catalog=Karamem0.SampleApplication;Integrated Security=True;TrustServerCertificate=true"
    },
+   "ServiceBusConnection": "Endpoint=sb://{{service-name}}.servicebus.windows.net/;SharedAccessKeyName={{access-key-name}};SharedAccessKey={{access-key}}",
+   "ServiceBusSender": {
+     "QueueOrTopicName": "SampleApplication"
+   },
  ...
}

実行結果

実際にデバッグして Swagger から POST すると Azure Service Bus にキューが入るのがわかります。本文には追加したアイテムの情報が入っています。

カスタム プロパティも設定されています。

おわりに

今回のようなシンプルな変更通知の他に、分散システムで使用できる Transactional Outbox パターンも利用できます。詳しくは以下のドキュメントをご確認いただければと思います。

SQL Server Event Outbox

Discussion