Skip to main content
added 13 characters in body
Source Link
Christophe
  • 82.2k
  • 11
  • 136
  • 202

I have a task where in I have to load certain object from the DB, and propagate it through few state and save after each state propagation.
Without giving away my client's domain, 

Let us assume it is aillustrate this with the example of an Order entity. I now have to load few pending orders from DB and Cancel them based on few criteria, if. If these criteria don't match I should not cancel them.
I

I have the below code in my Application layer:

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);
    }
}
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));
     }
    }
}

I have few classes which listen to the `OrderStatusChangedEvent`OrderStatusChangedEvent and performsperform other tasks like for example sending a notification and saving the updated status back to the database, etc.

If you observe the above `Order`Order class I don't have separate commands for each OrderStatus like `Cancel()` etcCancel() and is rather anaemic  .

I have it this way because I do not want to inject other commands and services into the `Order`Order class, because I have to perform `CanOrderBeCancelled` etcCanOrderBeCancelled for each status change.
From

From what I have currently it is pretty straight forward for me to introduce a new `Command`Command class for each status if required and inject other services in those classes leaving my `Order`Order class pure.

    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));
        }
      }
    }

The above approach has well defined methods on the `Order`Order class but for ideal separation of concerns I'll have to inject a command factory, and each commands would call the `internal` `SetOrderStatus`internal SetOrderStatus method like earlier. The commands without being injected to the `IOrderService`IOrderService will now be injected to the `Order`Order themselves.

I can also inject a `IOrderService`IOrderService into the Order class instead of the command factory and my class would look like below:

    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));
      }
    }
  }

Also my ApplicationApplication class will look like below:

    public class OrderCancelApplicaiton{
      public void CancelPendingOrders(){
        var pendingOrders = _orderRepository.GetPendingOrders();
        foreach (var pendingOrder in pendingOrders)
        {
            pendingOrder.Cancel();
        }
      }
    }

Finally I can also think of not having any commands which check if an OrderOrder can be cancelled, but rather have an additional OrderStatusChanged event handler that checks if the Order can indeed be cancelled, if not move it back to the previous state, the problem with this approach is that the 'Order' object will remain in an invalid state even if it is in-memory and momentarily before it is moved back to its previous state if it should not be cancelled. Are

Are you comfortable injecting Commands and Services into your domain models ?  
What would you have done and what is the prescribed method for such problems ?
Would you rather Cancel on the domain object and let a handler catch it and 'Revert' it back if it should not have been cancelled ?

I have a task where in I have to load certain object from the DB, and propagate it through few state and save after each state propagation.
Without giving away my client's domain, Let us assume it is a Order entity I now have to load few pending orders from DB and Cancel them based on few criteria, if these criteria don't match I should not cancel them.
I have the below code in my Application layer

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);
    }
}
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));
     }
    }
}

I have few classes which listen to the `OrderStatusChangedEvent` and performs other tasks like sending a notification and saving the updated status back to the database, etc.

If you observe the above `Order` class I don't have separate commands for each OrderStatus like `Cancel()` etc and is rather anaemic  

I have it this way because I do not want to inject other commands and services into the `Order` class, because I have to perform `CanOrderBeCancelled` etc for each status change.
From what I have currently it is pretty straight forward for me to introduce a new `Command` class for each status if required and inject other services in those classes leaving my `Order` class pure.

    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));
     }
    }
}

The above approach has well defined methods on the `Order` class but for ideal separation of concerns I'll have to inject a command factory, and each commands would call the `internal` `SetOrderStatus` method like earlier. The commands without being injected to the `IOrderService` will now be injected to the `Order` themselves.

I can also inject a `IOrderService` into the Order class instead of the command factory and my class would look like below

    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));
     }
    }
}

Also my Application class will look like below

    public class OrderCancelApplicaiton{
    public void CancelPendingOrders(){
        var pendingOrders = _orderRepository.GetPendingOrders();
        foreach (var pendingOrder in pendingOrders)
        {
            pendingOrder.Cancel();
        }
    }
}

Finally I can also think of not having any commands which check if an Order can be cancelled, but rather have an additional OrderStatusChanged event handler that checks if the Order can indeed be cancelled, if not move it back to the previous state, the problem with this approach is that the 'Order' object will remain in an invalid state even if it is in-memory and momentarily before it is moved back to its previous state if it should not be cancelled. Are you comfortable injecting Commands and Services into your domain models ?  
What would you have done and what is the prescribed method for such problems ?
Would you rather Cancel on the domain object and let a handler catch it and 'Revert' it back if it should not have been cancelled ?

I have to load certain object from the DB, and propagate it through few state and save after each state propagation.
 

Let us illustrate this with the example of an Order entity. I now have to load few pending orders from DB and Cancel them based on few criteria. If these criteria don't match I should not cancel them.

I have the below code in my Application layer:

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);
    }
}
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));
     }
    }
}

I have few classes which listen to the OrderStatusChangedEvent and perform other tasks like for example sending a notification and saving the updated status back to the database.

If you observe the above Order class I don't have separate commands for each OrderStatus like Cancel() and is rather anaemic.

I have it this way because I do not want to inject other commands and services into the Order class, because I have to perform CanOrderBeCancelled for each status change.

From what I have currently it is pretty straight forward for me to introduce a new Command class for each status if required and inject other services in those classes leaving my Order class pure.

    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));
        }
      }
    }

The above approach has well defined methods on the Order class but for ideal separation of concerns I'll have to inject a command factory, and each commands would call the internal SetOrderStatus method like earlier. The commands without being injected to the IOrderService will now be injected to the Order themselves.

I can also inject a IOrderService into the Order class instead of the command factory and my class would look like below:

    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));
      }
    }
  }

Also my Application class will look like below:

    public class OrderCancelApplicaiton{
      public void CancelPendingOrders(){
        var pendingOrders = _orderRepository.GetPendingOrders();
        foreach (var pendingOrder in pendingOrders)
        {
            pendingOrder.Cancel();
        }
      }
    }

Finally I can also think of not having any commands which check if an Order can be cancelled, but rather have an additional OrderStatusChanged event handler that checks if the Order can indeed be cancelled, if not move it back to the previous state, the problem with this approach is that the 'Order' object will remain in an invalid state even if it is in-memory and momentarily before it is moved back to its previous state if it should not be cancelled.

Are you comfortable injecting Commands and Services into your domain models ?
What would you have done and what is the prescribed method for such problems ?
Would you rather Cancel on the domain object and let a handler catch it and 'Revert' it back if it should not have been cancelled ?

edited title
Link
Christophe
  • 82.2k
  • 11
  • 136
  • 202

Help me improve my solution; comment on how you would Improving the design of state transitions in a solutionbusiness application: the case of cancellation

Source Link

Help me improve my solution; comment on how you would design a solution

I have a task where in I have to load certain object from the DB, and propagate it through few state and save after each state propagation.
Without giving away my client's domain, Let us assume it is a Order entity I now have to load few pending orders from DB and Cancel them based on few criteria, if these criteria don't match I should not cancel them.
I have the below code in my Application layer

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);
    }
}

And my domain service like below

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));
     }
    }
}

I have few classes which listen to the `OrderStatusChangedEvent` and performs other tasks like sending a notification and saving the updated status back to the database, etc.

If you observe the above `Order` class I don't have separate commands for each OrderStatus like `Cancel()` etc and is rather anaemic

I have it this way because I do not want to inject other commands and services into the `Order` class, because I have to perform `CanOrderBeCancelled` etc for each status change.
From what I have currently it is pretty straight forward for me to introduce a new `Command` class for each status if required and inject other services in those classes leaving my `Order` class pure.

Other option

    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));
     }
    }
}

The above approach has well defined methods on the `Order` class but for ideal separation of concerns I'll have to inject a command factory, and each commands would call the `internal` `SetOrderStatus` method like earlier. The commands without being injected to the `IOrderService` will now be injected to the `Order` themselves.

I can also inject a `IOrderService` into the Order class instead of the command factory and my class would look like below

    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));
     }
    }
}

Also my Application class will look like below

    public class OrderCancelApplicaiton{
    public void CancelPendingOrders(){
        var pendingOrders = _orderRepository.GetPendingOrders();
        foreach (var pendingOrder in pendingOrders)
        {
            pendingOrder.Cancel();
        }
    }
}

Finally I can also think of not having any commands which check if an Order can be cancelled, but rather have an additional OrderStatusChanged event handler that checks if the Order can indeed be cancelled, if not move it back to the previous state, the problem with this approach is that the 'Order' object will remain in an invalid state even if it is in-memory and momentarily before it is moved back to its previous state if it should not be cancelled. Are you comfortable injecting Commands and Services into your domain models ?
What would you have done and what is the prescribed method for such problems ?
Would you rather Cancel on the domain object and let a handler catch it and 'Revert' it back if it should not have been cancelled ?