How to decide if a command should be used instead of an event?

I’m working on a basic eCommerce demo application that only has Sales and Finance services currently. Sales is responsible for accepting the placement of orders, and Finance is responsible for collecting payment when an order is placed.

Now I known there’s a “when” in there so I originally thought Sales publishes OrderPlaced event and Finance handles that event to initiate its work. However, I’ve seen examples where Sales sends a (Finance-owned) ProcessPayment command.

A command feels more appropriate if Sales ultimately owns the order state, or at least it would want to know payment processing will be processed and succeed or fail so it can decide if something else should happen with the order. An event assumes the publisher doesn’t care what happens with the event.

If I refer to this sequence diagram it also shows the OrderManager publishing an OrderReady event (similar to my OrderPlaced). Does this imply the OrderManager doesn’t care if the order is actually delivered, and if so, why would it need to subscribe to a ShipmentStarted event?

I’ve seen examples where Sales sends a (Finance-owned) ProcessPayment command.

This gets right to the heart of this. With a command, you have a tighter coupling between the services, because now the Finance service needs to know all the information/data required to send the specific command and it needs to know where it has to send it. In addition, you also likely have binary dependencies due to the message contracts provided by Finance that couple the services further. With every additional endpoint going to be involved at some point, the Sales services has to send more and more commands and will couple to more and more dependencies.

Typically, events are used for cross-service communication. Any service can subscribe to the same business event and while the finance service might start payment processing, the warehouse service might already prepare the order or another service checks if the customer became eligible for a new customer status and so on, the sales service doesn’t care about this. Prefer events to decouple autonomous services. Use commands primarily within the same service (please keep in mind that a service in this regards is not a one-to-one mapping to an endpoint. There could be many endpoints within the same service).

An event assumes the publisher doesn’t care what happens with the event.

and the publisher itself probably also shouldn’t. It’s specifically the saga (that might probably be started by said event) that has knowledge of the business process and knows what should happen with the events. That saga could at some point move to another service or be split into multiple sagas handling different, more focused processes. You’d also have to ask yourself why exactly the sales service cares about shipping in the first place.

Events are much more powerful than commands and I’d recommend using events whenever possible when interacting between different business services.

disclaimer: Everything I wrote is just a very generic (and personal) recommendation and there is a huge “it depends” that you can put before every answer :wink: This topic is very much related to the content of the Advanced Distributed Systems Design course that you might find very interesting: Learn Advanced Distributed Systems Design • Particular Software

I’m not sure I understand your question. The sequence diagrams shows how the saga in the order manager does care if the order is delivered by handling ShipmentFailed / ShipmentStarted events?

Thank you for your response Tim.

I totally glossed over the fact that if some service subscribes to an event then it obviously cares about the occurence of that event. My mistake.

What I think I was missing was that whilst Sales is interested in knowing when Finance has finished processing an order payment, with a good or bad outcome, it doesn’t care when the processing occurs or is triggered. Sales should assume Finance will do what it needs to do, whether that is triggered by a pivotal event like OrderPlaced or something else. Bottom line is, Finance won’t be told when to start processing the payment by some other service. It decides for itself.

As for commands only being used inter-service rather than cross-service I agree for sure. I’ve always had problems grasping this in the past as for a long time I never really understood the concept of a service as a logical thing that can have many components that indeed can talk to each other, and that this doesn’t necessarily contribute 1-to-1 to deployment architecture.

I’ve followed some of Udi’s course material and also View Model Composition strategies (shout out to Mauro Servienti for his work in this area) and it was enlightening in terms of thinking about decoupling the responsibility of who owns what. For example, whilst a mobile app backend or a webapp might want to tell other services about some user action, it doesn’t own the “how” it is achieved or “which” services might handle the request or message.

If I may, can I ask about your thoughts on sagas publishing events as opposed to only sending commands? I’ve followed a pattern comprised of the following implementation “rules” in terms of NServiceBus and handlers in general:

  1. Command handlers can make domain state changes and publish an event - for eventual consistency.
  2. Command handlers can call an idempotent external API and publish an event.
  3. Command handlers cannot change domain state, call an external API and publish an event.
  4. Event handlers can only send commands.
  5. Sagas can only by triggered by and handle events.
  6. Sagas can only send commands or reply to sender.

With my rules it implies a saga can only orchestrate activity based upon event information only. However sagas can have state and if that state changes due to handling an event in the saga you could argue that it’s perfectly valid for the saga to publish this fact, rather than sending a command that perhaps does nothing other than publish that same event.

I’m tending towards a don’t-complicate-things approach and allow the saga to publish events in the cases where no other side-effect is required. Is this okay or am I’m missing something?

I view sagas as stateful handlers so I see no issue with them publish events or them being started by commands,

Have you read Putting your events on a diet • Particular Software which goes through a very similar scenario to what you describe and gives a few points on how to design your events?

1 Like

Good to know you’ve had no issues.

I did read the article a long time ago, thank you. I found it useful for sure. And I always strive to keep only IDs in my events.