Question

I have been pondering this for a while. In general I try to stay away from injecting services into my domain, but I have this case:

I have an PurchaseOrder object. This order is sent to a supplier using some service (Email or webservice). After the order is sent, a confirmation should be sent to the user that made the order.

So I realised this is a case where Domain Events would be a nice way of implementing this publishing an PurchaseOrderMade - event.

Then I came to think:

Is the order really made if the order wasnt sent?

You haven't made an order just because you decided to do it and wrote it down, but the order is made at the time when you have conveyed it to the supplier according to contract without errors.

So I redecided and thought that this might belong to the domain after all so I should send it by injecting a IPurchaseOrderSender to my domain and then publish an OrderMadeEvent after the successfull transaction and sending a confirmation in the EventHandler.

The reasoning is this:

  1. The sending of the order IS crucial part of the process and could cause state to alter (i.e. setting a flag that the order is sent)
  2. The confirmation is NOT crucial, should this fail the order is still made and everything would go as planned.
  3. It is easy to read, and to alter implementation of an IOrderService

The questions are:

  1. Is this really so bad to do?
  2. Does it break principles of DDD?
  3. Have you encountered this before and solved it in a better way?

Here is the code:

        public void MakeOrder(PurchaseOrder order, IPurchaseOrderSender orderSender)
    {
        if(PurchaseOrders == null)
            PurchaseOrders = new List<PurchaseOrder>();
        PurchaseOrders.Add(order);

        orderSender.Send(order);
        DomainEvents.Raise(new PurchaseOrderIsMade(){Order = order});
    }

    public interface IPurchaseOrderSender
{
    void Send(PurchaseOrder order);
}
Was it helpful?

Solution

I encountered this before, here is what I did:

Split the local transaction with the remote procedure call.

I think it's not a big deal if the order sending fails. In this case, either the order is placed but not set to "sent" or the order is rollbacked. The business operator can intervene if the order is not send or the customer will call if the order is not placed.

But it's annoying if there is something wrong with the transaction after sending the order successfully. In this case, if the order is rollbacked, the intervention is more difficult because we lost the supplier's notification. The notification usually contains an supplier's order identifier, therefore we could cancel the order with this identifier if necessary.

So we decide to use messaging.
1) The PlaceOrderService is responsible for storing the order and sends a message.
2) The consumer of the message sends the order to the suppler and sends a message containing the supplier's notification.
3) The other consumer of the notification message update the order state.

Each step modifies only one aggregate or just call the remote.

Hope this helps.

Update

1.How would you implement the messaging part here

I adopt the solution mentioned in Eric Evans' dddsample, ApplicationEvents. It's just a simple interface and an jms implementation, something like

    public void placeOrder(...) {// method in application service
        ....//order making
        orderRepository.store(order);
        applicationEvents.orderWasPlaced(order);//injected applicationEvents 
        //better move this step out of transaction boundary if not using 2pc commit
        //make the method returnning order and use decorator to send the message 
        //    placeOrder(...) {
        //        Order order = target.placeOrder(...);//transaction end here
        //        applicationEvents.orderWasPlaced(order);
        //        return order;
        //    }
    }

    public class JmsApplicationEvents implements ApplicationEvents {
        public void orderWasPlaced(Order order) {
             //sends the message using messaging api of your platform
        }
    }

2.I see you mention suppliers notification, but let's assume this is done through email (which will be the primary scenario here) I would like to know that the transaction was performed without errors (i.e. no smtp or connection failure), but cannot rely on a response that the order was actually received would that change anything?

hmm.. I have never build an trading application based on email, but here is my suggestions:

  1. Messaging solution still fits if you need strong consistency. Messaging is transactional and could involve in global transaction while email doesn't.

  2. Messaging provides more availability (your order making will not fail even if the mailserver is down) and scalability(queues).

  3. Failure handling is more difficult in messaging solution, usually needs compensating actions. And it's more difficult for the user to get the processing information. For example, you have to notify the user about the progress of the order processing since the following steps are asynchronous. And an email should be sent to the customer if the order is rejected by the supplier.

  4. But messaging does add extra complexity and takes you more effort to build and maintain. You have to evaluate is the gain worth the cost. Actually, I have also built several systems in synchronous solution(they don't require high throughput and availability), they works fine most of the time, only less than ten of orders fail due to the connection problem within a year, so it's not worthy to build an automatic error handling mechanism at all.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top