Avanade Beef のレイヤー構造を理解する
はじめに
Avanade Beef (以下、Beef) は ASP.NET Core をベースとする Web API の自動生成ツールです。
概要については以下のスライドもご覧ください。
Beef を理解するには、レイヤーの構造を把握することが非常に重要です。基本的には以下の図に示されていますが、英語なので分かりにくい場合もあるため、概要を解説します。

なお今回の記事では、自動生成されるコードの例は見やすさのために一部改変しています。
サービス インターフェイス層 (XxxController)
サービス インターフェイス層は、ドメイン ロジックのファサード (外観) となるレイヤーです。Api プロジェクトに XxxController という名前のクラスが作成されます。これは ASP.NET Core MVC のコントローラーと同等です。このクラスは外部からパラメーターを受け取り、下層のドメインロジック (XxxManager) を呼び出し、HTTP ステータスコードおよび結果のコンテンツ (JSON) を返します。メソッドを追加してカスタマイズ (たとえば JSON ではなく XML を返すなど) が可能です。
以下は自動生成されるコードの例です。
namespace Foo.Bar.Api.Controllers
{
[Route("persons")]
public partial class PersonController : ControllerBase
{
private readonly IPersonManager _manager;
public PersonController(IPersonManager manager)
{ _manager = Check.NotNull(manager, nameof(manager)); PersonControllerCtor(); }
[HttpGet("{id}")]
[ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public IActionResult Get(Guid id) =>
new WebApiGet<Person?>(this, () => _manager.GetAsync(id),
operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NotFound);
}
}
ドメイン ロジック層 (XxxManager)
ドメインロジック層は、ビジネス ドメインやワークフロー ロジックを提供するレイヤーです。Business プロジェクトに XxxManager という名前のクラスが作成されます。このクラスは必要なクリーンアップ (文字列や日時の Trim など) を行い、下層のサービスオーケストレーション (XxxDataSvc) を呼び出します。アイテムの作成時に ID となる GUID の生成も行います。
以下は自動生成されるコードの例です。
namespace Foo.Bar.Business
{
public partial class PersonManager : IPersonManager
{
private readonly IPersonDataSvc _dataService;
public PersonManager(IPersonDataSvc dataService, IGuidIdentifierGenerator guidIdentifierGenerator)
{ _dataService = Check.NotNull(dataService, nameof(dataService)); _guidIdentifierGenerator = Check.NotNull(guidIdentifierGenerator, nameof(guidIdentifierGenerator)); PersonManagerCtor(); }
public Task<Person?> GetAsync(Guid id) => ManagerInvoker.Current.InvokeAsync(this, async () =>
{
Cleaner.CleanUp(id);
await id.Validate(nameof(id)).Mandatory().RunAsync(throwOnError: true).ConfigureAwait(false);
return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false));
}, BusinessInvokerArgs.Read);
}
}
サービス オーケストレーション層 (XxxDataSvc)
サービス オーケストレーション層は、データ アクセスのオーケストレーションを提供するレイヤーです。Business プロジェクトに XxxDataSvc という名前のクラスが作成されます。このクラスはデータキャッシュを検索し、キャッシュがあればその値を、キャッシュがなければ下層のデータアクセス (XxxData) を呼び出します。キャッシュの有効期間は webapisettings.json で設定できます。
以下は自動生成されるコードの例です。
namespace Foo.Bar.Business.DataSvc
{
public partial class PersonDataSvc : IPersonDataSvc
{
private readonly IPersonData _data;
private readonly IRequestCache _cache;
public PersonDataSvc(IPersonData data, IRequestCache cache)
{ _data = Check.NotNull(data, nameof(data)); _cache = Check.NotNull(cache, nameof(cache)); PersonDataSvcCtor(); }
public Task<Person?> GetAsync(Guid id) => DataSvcInvoker.Current.InvokeAsync(this, async () =>
{
var __key = new UniqueKey(id);
if (_cache.TryGetValue(__key, out Person? __val))
return __val;
var __result = await _data.GetAsync(id).ConfigureAwait(false);
return _cache.SetAndReturnValue(__key, __result);
});
}
}
データ アクセス層 (XxxData)
データ アクセス層は、データ ソースへのアクセス (ストアド プロシージャの呼び出しなど) を提供するレイヤーです。Business プロジェクトに XxxData という名前のクラスが作成されます。このクラスは Cosmos DB、SQL Database、OData などのデータソースを呼び出します。データを取得する場合はクエリの作成も行います。メソッドを追加してカスタマイズ (前述した以外のデータソースに接続するなど) が可能です。
以下は自動生成されるコードの例です。
namespace Foo.Bar.Business.Data
{
public partial class PersonData : IPersonData
{
private readonly IEfDb _ef;
private readonly AutoMapper.IMapper _mapper;
private readonly IEventPublisher _evtPub;
public PersonData(IEfDb ef, AutoMapper.IMapper mapper, IEventPublisher evtPub)
{ _ef = Check.NotNull(ef, nameof(ef)); _mapper = Check.NotNull(mapper, nameof(mapper)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); PersonDataCtor(); }
public Task<Person?> GetAsync(Guid id) => DataInvoker.Current.InvokeAsync(this, async () =>
{
var __dataArgs = EfDbArgs.Create(_mapper);
return await _ef.GetAsync<Person, EfModel.Person>(__dataArgs, id).ConfigureAwait(false);
});
}
}
サービス エージェント層 (XxxAgent)
サービス エージェント層は、サービス インターフェイス層の呼び出しのプロキシを提供します。主な用途はテスト プロジェクトでサービス インターフェイス (Controller) の呼び出しを簡潔にすることです。
エンティティ (DTO)
エンティティはビジネスロジックを含みませんが、ICloneable、ICopyFrom、IEquatable などの機能を提供します。
おわりに
Beef の特長は自動生成されるコードをパーシャ ルクラスを使うことで比較的自由にカスタマイズできる点です。ただし、レイヤーの構造を知らないと困ることもあります。このあたりを理解しておくとカスタマイズがしやすくなります。
Discussion