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?