Eu sei que você está pensando (ou talvez gritando) ", nenhuma outra pergunta perguntando onde a validação pertence a uma arquitetura em camadas?!?" Bem, sim, mas espero que isso seja um pouco diferente do assunto.
Acredito firmemente que a validação assume muitas formas, é baseada em contexto e varia em cada nível da arquitetura. Essa é a base para o pós - ajudar a identificar que tipo de validação deve ser realizada em cada camada. Além disso, uma pergunta que costuma surgir é onde pertencem as verificações de autorização.
O cenário de exemplo vem de um aplicativo para uma empresa de catering. Periodicamente, durante o dia, o motorista pode entregar ao escritório qualquer excesso de dinheiro acumulado ao levar o caminhão de um local para outro. O aplicativo permite que o usuário registre o 'dinheiro' coletando a identificação do motorista e o valor. Aqui está um código de esqueleto para ilustrar as camadas envolvidas:
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
...
}
}
Eu indiquei 10 locais onde vi verificações de validação inseridas no código. Minha pergunta é: quais verificações você executaria, se houver alguma, em cada uma das seguintes regras de negócios (juntamente com verificações padrão de comprimento, intervalo, formato, tipo etc.):
- O valor da queda de caixa deve ser maior que zero.
- O saque em dinheiro deve ter um driver válido.
- O usuário atual deve estar autorizado a adicionar retiradas de caixa (o usuário atual não é o motorista).
Compartilhe seus pensamentos, como você tem ou abordaria esse cenário e os motivos de suas escolhas.
CashDropAmount
objeto de valor em vez de usar a Decimal
. Verificar se o driver existe ou não seria feito no manipulador de comandos e o mesmo vale para as regras de autorização. Você pode obter autorização de graça, fazendo algo como Approver approver = approverService.findById(employeeId)
onde ele joga, se o funcionário não estiver na função de aprovador. Approver
seria apenas um objeto de valor, não uma entidade. Você também pode se livrar de sua fábrica ou uso método de fábrica em uma AR em vez disso: cashDrop = driver.dropCash(...)
.