Я знаю, що ви думаєте (чи, може, кричите), "не інше питання, де валідація належить до багатошарової архітектури?!?" Ну так, але, сподіваємось, це буде дещо по-іншому сприйняти цю тему.
Я твердо вірю, що валідація приймає багато форм, ґрунтується на контексті та змінюється на кожному рівні архітектури. Це є підставою для публікації, яка допомагає визначити, який тип перевірки слід виконувати в кожному шарі. Крім того, питання, яке часто виникає, - це те, куди належать перевірки авторизації.
Приклад сценарію виходить з програми для підприємств громадського харчування. Періодично протягом дня водій може повернути в офіс будь-які надлишки грошей, які вони накопичили під час перевезення вантажівки з місця на місце. Додаток дозволяє користувачеві записувати «падіння грошових коштів» шляхом збору посвідчення водія та суми. Ось код коду скелета для ілюстрації задіяних шарів:
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(...)
.