Tuesday, April 8, 2008

When Event Messages are not Ideal

In a recent post, I discussed why we should prefer the use of event messages over command messages. There are however situations where command messages are more appropriate.

Say for instance we have a billing service that has the ability to raise invoices. Let us also say that there in a large number other services in the enterprise supporting business processes that at some point require an invoice to be raised. Let us then assume that the action of raising the invoice is to be exactly the same, or at least remarkably similar in all cases.

Using event messages in this scenario would lead us to having the billing service subscribe to different event messages from a number of different services, all of which resulting in the exact same operation in the billing service. This is is somewhat wasteful as the billing service needs to be aware of and process a large number of event message types.

As the required behaviour of the raise invoice operation will not vary between contexts, the context of the originating process is mostly irrelevant. This makes the raise invoice operation of the billing service highly reusable.

As such, we should strongly consider using a single command message sent by all other services to the billing service to invoke the raise invoice operation.

We need to be careful however that the reuse here is mandated by the business, rather than coincidental. If we find that in the future each of these services start having different needs of the billing service in raising invoices, we will greatly overcomplicate this operation and start needing to update the command message structure and/or billing service implementation to compensate. We start suffering the effects of coupling again.

In the field we tend to find that the context of the originating process influences the behaviour of the target service most of the time. Where it doesn't initially, we tend to find that it does at some point in the future. That is why it is best in general to prefer the use of event messages over command messages.

7 comments:

flgb said...

Perhaps there is an alternative to the billing service having to understand many different types of events.

Perhaps the billing service is just interested in one type of event, billable events, that is: this product was just purchased, this service was just used, this contract was just extended, and so on.

This allows the billing service to retain responsibility for knowing when and how to invoice and so avoids the problem of command messages.

Bill said...

The example events you provided (this product was just purchased, this service was just used, this contract was just extended) constitute 3 different event message types.

The billing service would need to know how to interpret each of these message types, each resulting in the same operation.

flgb said...

I was trying to imply we could do type polymorphism.

Bill said...

Even if our 3 event message types inherit from a base message type, the billing service is still going to need to understand the structure of each of the derived types as it would have to extract data from message elements defined in those derived types.

Bill Pierce said...

Hey Bill,
I've read your posts for the last two months and I'm interested in learning more about SOA but the area I struggle with most is defining the service boundary.

I'm using the following scenario for leanring. We have a number of third party systems that we want to accept orders from. I envision some sort of "order created" or "order received" event message starting the order process.

One of our business rules is that an order cannot be shipped to a location we do not already have on file. Part of the order message would include a "ShipTo Id". We do not yet expose any sort of service for third parties to manage or update these addresses. They need to call in to our help desk to add a new address or change an existing address.

Are we going about this in the right way or am I stuck in an OO anti-pattern?

Second how do we expose the Shipping Locations we have on file for the third parties to use when sending an order without some sort of CRUD "GetShippingAddressesOnFile" called prior to submitting the order?

How should we look at the problem differently to reduce coupling and not end up dis-enchanted with SOA?

At the same time, how can we do this in a phased approach without immediately starting with some sort of pub/sub for all required order information e.g. Having third parties consume a ShippingAddressUpdated event message, keeping a de-centralized copy of this data so they no longer have to query us for it when submitting an order. While this would be ideal, it would require a much larger investment for each third-party.

How can we incrementally get there?

Colin Scott said...

I'd be automatically wary about trying to enforce your way of doing things on clients. You don't want to make it difficult for people to give you money. As such I'd question the need to make the customers participate in your SOA. There are plenty of perfectly good ways of distributing this data that would be easier for a client to work with. From an SOA perspective the interactions with the clients occur behind one or more of your service boundaries, so constraints that apply to communication between your services do not necessarily apply.

If the data changed relatively infrequently you could consider a data file (XML, CSV, Excel etc) that you throw up in a known location on a website. Email updates to clients that it has been updated could then satisfy the need to notify them of changes. This is lightweight and widely supported.

Alternatively you could expose a web service to supply the information. This would be exposed to the world by one of the services in your SOA. The other services in the SOA don't need to know or care that it exists, it's essentially a private endpoint from their perspective. This is more suitable if the data size is relatively constrained and the data changes often. In my opinion this is only notionally CRUD, in that you're not accepting any create, updates or deletes on this data which means you're not exporting business logic related to the data to other systems.

Part of the problem is the ambiguous use of "service". We have services in an SOA, which external entities don't need to care about. These may provide services to external entities (such as a list of shipping locations). The external entities don't care when consuming a service what SOA service provides it. They just want service. I don't think this problem will go away until we come up with unique terms for all of these things (and get the terms widely adopted).

Bill said...

@ Bill Pierce

Good questions Bill. I would consider your Ordering service to be a distributed service, where the third party systems that send requests to create orders as inside the Ordering service boundary.

The endpoint(s) that they use to communicate with the Ordering service are private in that they will not be consumed by other services in your enterprise.

One way you might go about dealing with customers that don't yet have a registered address, is you could allow your "create order" message sent by your third parties to either stipulate a ShipTo ID if they already have one, or provide the full unregistered address.

If no ShipTo ID was provided, your service could fire off a workflow inside your organisation, informing your help desk that a customer would like to place an order with delivery to an unregistered location.

Your help desk worker could then look up the address and if it is not already registered, create the new ShipTo ID.

Once the ShipTo ID has been assigned to the order, the Ordering service would then go on about its business as it would have done had a ShipTo ID been originally provided by the third party.

At this point, the Ordering service would raise an "order received" or "order created" event, which would contain the ShipTo ID.

Please keep those questions coming!