「検証が階層化アーキテクチャのどこに属しているのかを尋ねる別の質問ではありませんか?!?」ええ、はい、しかし、これがこの問題に対する少し異なる見解になることを願っています。
私は、検証は多くの形式を取り、コンテキストベースであり、アーキテクチャの各レベルで異なると固く信じています。これが投稿の基礎です。各レイヤーで実行される検証のタイプを識別するのに役立ちます。また、よくある質問は、承認チェックの場所です。
サンプルシナリオは、ケータリングビジネスのアプリケーションからのものです。日中定期的に、運転手は、トラックを現場から現場へ移動する間に蓄積した余分な現金をオフィスに引き入れることができます。このアプリケーションでは、ユーザーはドライバーのIDと金額を収集することにより、「キャッシュドロップ」を記録できます。関連するレイヤーを説明するスケルトンコードを次に示します。
public class CashDropApi // This is in the Service Facade Layer
{
[WebInvoke(Method = "POST")]
public void AddCashDrop(NewCashDropContract contract)
{
// 1
Service.AddCashDrop(contract.Amount, contract.DriverId);
}
}
public class CashDropService // This is the Application Service in the Domain Layer
{
public void AddCashDrop(Decimal amount, Int32 driverId)
{
// 2
CommandBus.Send(new AddCashDropCommand(amount, driverId));
}
}
internal class AddCashDropCommand // This is a command object in Domain Layer
{
public AddCashDropCommand(Decimal amount, Int32 driverId)
{
// 3
Amount = amount;
DriverId = driverId;
}
public Decimal Amount { get; private set; }
public Int32 DriverId { get; private set; }
}
internal class AddCashDropCommandHandler : IHandle<AddCashDropCommand>
{
internal ICashDropFactory Factory { get; set; } // Set by IoC container
internal ICashDropRepository CashDrops { get; set; } // Set by IoC container
internal IEmployeeRepository Employees { get; set; } // Set by IoC container
public void Handle(AddCashDropCommand command)
{
// 4
var driver = Employees.GetById(command.DriverId);
// 5
var authorizedBy = CurrentUser as Employee;
// 6
var cashDrop = Factory.CreateCashDrop(command.Amount, driver, authorizedBy);
// 7
CashDrops.Add(cashDrop);
}
}
public class CashDropFactory
{
public CashDrop CreateCashDrop(Decimal amount, Employee driver, Employee authorizedBy)
{
// 8
return new CashDrop(amount, driver, authorizedBy, DateTime.Now);
}
}
public class CashDrop // The domain object (entity)
{
public CashDrop(Decimal amount, Employee driver, Employee authorizedBy, DateTime at)
{
// 9
...
}
}
public class CashDropRepository // The implementation is in the Data Access Layer
{
public void Add(CashDrop item)
{
// 10
...
}
}
コードに配置された検証チェックを見た10の場所を示しました。私の質問は、次のビジネスルール(長さ、範囲、形式、タイプなどの標準的なチェックと一緒に)を指定して、それぞれで実行するチェックがある場合、それを実行することです。
- キャッシュドロップの金額はゼロより大きくなければなりません。
- キャッシュドロップには有効なドライバーが必要です。
- 現在のユーザーには、キャッシュドロップを追加する権限が必要です(現在のユーザーはドライバーではありません)。
あなたの考え、あなたがこのシナリオをどのように持っているか、どのようにアプローチするか、そしてあなたの選択の理由を共有してください。
CashDropAmount
使用するのではなく、値オブジェクトを持つことで、ここで値オブジェクトを活用できると思いますDecimal
。ドライバーが存在するかどうかの確認はコマンドハンドラーで行われ、同じことが承認規則にも当てはまります。Approver approver = approverService.findById(employeeId)
従業員が承認者ロールにない場合にスローするようなことを行うことで、無料で認証を取得できます。Approver
単なる値オブジェクトであり、エンティティではありません。ファクトリを削除するか、代わりにARのファクトリメソッドを使用することもできますcashDrop = driver.dropCash(...)
。