Connecting Points Using DDD

I read Evans, Nilson and McCarthy, among others, and understood the concepts and arguments for domain-driven design; however, it is difficult for me to combine all this in a real application. The lack of complete examples made me scratch my head. I found a lot of frameworks and simple examples, but so far nothing shows how to create a real business application after DDD.

Using a typical order management system, consider the case of canceling an order. In my design, I see an OrderCancellationService with the CancelOrder method, which takes order # and reason as parameters. Then he must follow these “steps”:

  • Make sure that the current user has the necessary permission to cancel the order
  • Get Order object with specified order # from OrderRepository
  • Make sure the order can be canceled (if the service asks for the order status to evaluate the rules or if the Order has a CanCancel property that encapsulates the rules?)
  • Update the state of the Order object by calling Order.Cancel (reason)
  • Save updated order to data warehouse
  • Contact CreditCardService to return all credit card payments that have already been processed.
  • Add audit record for operation

, , . , , , . , imo, , CreditCardService (Order), , , .

-, , / "". , , , . !

+5
2

. , , . , (DIP). , . . , , "" ( ).

    class OrderCancellationService {

    private readonly ICreditCardGateway _creditCardGateway;
    private readonly IAuditor _auditor;

    public OrderCancellationService(
        ICreditCardGateway creditCardGateway, 
        IAuditor auditor) {
        if (creditCardGateway == null) {
            throw new ArgumentNullException("creditCardGateway");
        }
        if (auditor == null) {
            throw new ArgumentNullException("auditor");
        }
        _creditCardGateway = creditCardGateway;
        _auditor = auditor;
    }

    public void Cancel(Order order) {
        if (order == null) {
            throw new ArgumentNullException("order");
        }
        // get current user through Ambient Context:
        // http://blogs.msdn.com/b/ploeh/archive/2007/07/23/ambientcontext.aspx
        if (!CurrentUser.CanCancelOrders()) {
            throw new InvalidOperationException(
              "Not enough permissions to cancel order. Use 'CanCancelOrders' to check.");
        }
        // try to keep as much domain logic in entities as possible
        if(!order.CanBeCancelled()) {
            throw new ArgumentException(
              "Order can not be cancelled. Use 'CanBeCancelled' to check.");
        }
        order.Cancel();

        // this can throw GatewayException that would be caught by the 
        // 'Cancel' caller and rollback the transaction
        _creditCardGateway.RevertChargesFor(order);

        _auditor.AuditCancellationFor(order);
    }
}
+2

:

//UI
public class OrderController
{
    private readonly IApplicationService _applicationService;

    [HttpPost]
    public ActionResult CancelOrder(CancelOrderViewModel viewModel)
    {
        _applicationService.CancelOrder(new CancelOrderCommand
        {
            OrderId = viewModel.OrderId,
            UserChangedTheirMind = viewModel.UserChangedTheirMind,
            UserFoundItemCheaperElsewhere = viewModel.UserFoundItemCheaperElsewhere
        });

        return RedirectToAction("CancelledSucessfully");
    }
}

//App Service
public class ApplicationService : IApplicationService
{
    private readonly IOrderRepository _orderRepository;
    private readonly IPaymentGateway _paymentGateway;

    //provided by DI
    public ApplicationService(IOrderRepository orderRepository, IPaymentGateway paymentGateway)
    {
        _orderRepository = orderRepository;
        _paymentGateway = paymentGateway;
    }

    [RequiredPermission(PermissionNames.CancelOrder)]
    public void CancelOrder(CancelOrderCommand command)
    {
        using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
        {
            Order order = _orderRepository.GetById(command.OrderId);

            if (!order.CanBeCancelled())
                throw new InvalidOperationException("The order cannot be cancelled");

            if (command.UserChangedTheirMind)
                order.Cancel(CancellationReason.UserChangeTheirMind);
            if (command.UserFoundItemCheaperElsewhere)
                order.Cancel(CancellationReason.UserFoundItemCheaperElsewhere);

            _orderRepository.Save(order);

            _paymentGateway.RevertCharges(order.PaymentAuthorisationCode, order.Amount);
        }
    }
}

:

  • , , / . , Customer, Order, OrderCancellationService, .
  • ( ) . , , ; , .
  • , - , . , CancelOrder. intercepter , , ( Thread.CurrentPrincipal).
  • , " ". (.. ), , , . , , /, - .
+2

All Articles