Devo caricare determinati oggetti dal DB e propagarli attraverso pochi stati e salvare dopo ogni propagazione di stato.
Cerchiamo di illustrare questo con l'esempio di un'entità Order .
Ora devo caricare pochi ordini in sospeso da DB e Cancel in base a pochi criteri. Se questi criteri non corrispondono, non dovrei cancellarli.
Ho il codice seguente nel mio strato Application :
public void CancelPendingOrders() {
var pendingOrders = _orderRepository.GetPendingOrders();
_orderService.CancelOrders(pendingOrders);
}
public class CancelOrderCommand {
//other classes which are used in OrderCanBeCancelled are injected in .ctor
public void Execute(Order order) {
if(CanExecute(order)) // OrderCanBeCancelled
order.SetStatus(OrderStatus.Cancelled);
}
}
E il mio servizio di dominio come sotto
public class OrderCancelService : IOrderCancel{
void CancelPendingOrders(Orders[] orders){
foreach (var order in orders)
{
_orderCancelCommand.Execute(order);
}
}
}
public class Order{
public Order(OrderId orderId, OrderStatus orderStatus){
// assigned to local private fields.
}
public event EventHandler<OrderStatusChangedEventArgs> OrderStatusChanged;
internal void ChangeOrderStatus(OrderStatus status){
var oldStatus = new OrderStatus(_status);
_status = status;
if(OrderStatusChanged != null){
OrderStatusChanged(this, new OrderStatusChangedEventArgs(oldStatus,_status));
}
}
}
Ho poche classi che ascoltano OrderStatusChangedEvent ed eseguono altre attività come ad esempio l'invio di una notifica e il salvataggio dello stato aggiornato nel database.
Se osservi la precedente classe Order non ho comandi separati per ogni OrderStatus come Cancel() ed è piuttosto anemica.
Ce l'ho in questo modo perché non voglio iniettare altri comandi e servizi nella classe Order , perché devo eseguire CanOrderBeCancelled per ogni cambio di stato.
Da quello che ho attualmente è abbastanza semplice per me introdurre una nuova classe Command per ogni stato se necessario e iniettare altri servizi in quelle classi lasciando la mia Order class pure.
Altra opzione
public class Order{
public Order(OrderId orderId, OrderStatus orderStatus, ICommandFactor orderCommandFactory){
//assigned to local private fields.
}
public event EventHandler<OrderStatusChangedEventArgs> OrderStatusChanged;
public void Cancel(){
_orderCommandFactory.GetCancelCommand().Execute(this);
}
public void Success(){
_orderCommandFactory.GetSuccessCommand().Execute(this);
}
internal void SetStatus(OrderStatus status){
var oldStatus = new OrderStatus(_status);
_status = status;
if(OrderStatusChanged != null){
OrderStatusChanged(this, new OrderStatusChangedEventArgs(oldStatus,_status));
}
}
}
L'approccio sopra descritto ha metodi ben definiti sulla classe Order ma per una separazione ideale delle preoccupazioni dovrò iniettare un factory di comandi, e ogni comando chiamerebbe il metodo internal SetOrderStatus come prima. I comandi senza essere iniettati in IOrderService verranno ora iniettati su Order .
Posso anche inserire un IOrderService nella classe Order al posto del factory di comando e la mia classe sarebbe la seguente:
public class Order{
public Order(OrderId orderId, OrderStatus orderStatus, IOrderService orderService){
//assigned to local private fields.
}
public event EventHandler<OrderStatusChangedEventArgs> OrderStatusChanged;
public void Cancel(){
if(orderService.CanCancel(this)){
SetStatus(OrderStatus.Cancel);
}
}
public void Success(){
if(orderService.IsSuccessful(this)){
SetStatus(orderStatus.Success);
}
}
internal void SetStatus(OrderStatus status){
var oldStatus = new OrderStatus(_status);
_status = status;
if(OrderStatusChanged != null){
OrderStatusChanged(this, new OrderStatusChangedEventArgs(oldStatus,_status));
}
}
}
Anche la mia classe Application avrà il seguente aspetto:
public class OrderCancelApplicaiton{
public void CancelPendingOrders(){
var pendingOrders = _orderRepository.GetPendingOrders();
foreach (var pendingOrder in pendingOrders)
{
pendingOrder.Cancel();
}
}
}
Infine posso anche pensare di non avere alcun comando che controlli se un Order può essere cancellato, ma piuttosto un addizionale OrderStatusChanged gestore di eventi che controlla se il Order può effettivamente essere cancellato, se non lo muove tornando allo stato precedente, il problema con questo approccio è che l'oggetto 'Ordine' rimarrà in uno stato non valido anche se è in memoria e momentaneamente prima che venga riportato al suo stato precedente se non dovrebbe essere cancellato.
Sei comodo nell'iniettare Commands e Services nei tuoi modelli di dominio?
Cosa avresti fatto e qual è il metodo prescritto per tali problemi?
Preferiresti Cancel sull'oggetto dominio e permetti a un gestore di prenderlo e di "ripristinarlo" se non dovrebbe essere stato cancellato?