More of my notes on Udi Dahan‘s podcast on – Does SOA requires ESB?
Does SOA require ESB?
A single system that will be divided by autonomous services.
Autonomous services as opposed to simple services, that a lot of the industry is talking about, add another layer of richness into what goes into a service and what a service’s behavior is. If we were to take a concrete example of what the difference between a simple service and an autonomous services is, an example of simple service would be something like address validation service – which is a passive kind of service that while provides a much needed functionality doesn’t contain any data of its own and doesn’t really do any work on its own, until someone asks it a question. Whereas an autonomous service example would be a marketing service or sales service or a business level service, that even if no one asks it to do anything, has its own business, has its own data, does its own kind of thing when it needs to do it.
We can see the different kind of behavior and the different kind of interaction we’d have with each one of those services.
While with a simple service, passive service if you will, the kind of interaction we have is pretty much low level request and response – gimme something and the service gives it back.
With a business level services, an autonomous service, the kinds of interactions change quite a bit, since an autonomous service has to behave a certain way, has its own data, that has to version and manage, wont just let anybody come in and mingle its own data. The kinds of requests its exposes to the outside world, its message schema would be quite a bit different, obviously richer in the number of message types it would be willing to accept. So we are talking about two totally different scales of service. So in a context of distributed system, when we go about dividing that system into services, I would suggest going the autonomous services route, and not focusing so much on system level of low level passive services.
So once we go to that level of primary building block, for building a distributed system and we recognize that a unit of distribution is an autonomous service. That is not to say that a single autonomous service has to run in a single autonomous server. A single autonomous service can run on many servers, it could run on an entire datacenter, it could run on multiple datacenters. Rather the way that we wanna look at it is saying – things that we are gonna distribute are going to be autonomous services.
The opposite of that is that if it is not an autonomous service, we will not be distributing it in the same way that we distribute autonomous services. This is very important to prevent us from going down to the distributed objects path of creating tiny little distributed objects that chat amongst themselves over the network as if everything was done in memory.
Now, what exactly does an autonomous allow you to do with it. The message schema than an autonomous service exposes is exactly that, the message schema belongs to the service. You will not be sending these messages to another autonomous service, there are not general purpose messages that happen to be floating around in the air. A message is defined by the autonomous service that exposes it, that same autonomous services has an endpoint or many endpoints where it is willing to accept those messages, other autonomous services have their own messages.
So you wouldn’t be having the same message going to a number of autonomous services. So going back to the distributing thing, when you as a client or another service need to communicate with other autonomous services, the very act of saying – I’m going to send this message, you already know where it has to be sent to. So it becomes an implementation detail, where that autonomous service’s endpoints are is not an application level concern for you. You just wanna say, send this message, where it is supposed to sent to is defined in the implementation detail of your higher level bus type layer. That bus would know that this kind of message needs to be sent to that autonomous service, and that autonomous services has these endpoints and it will send it to that endpoint.
So we’ve simplified the kinds of communications we have, everything is a message, you can’t do a RPC with an autonomous service. Simply because the autonomous service just won’t let you. Autonomous service will not be holding resources open and threads just waiting around for you to do a whole bunch of tidy conversations with it. You need to bundle up everything you need to say in a message, you send it to the service and if it feels like it, it will send you a message back at some point in the future – but don’t hold your breath.
So getting back to issue of the bus, what is a bus and what do we need the bus to do?
First, when we are talking about the API which we want the bus to expose, we’ve already identified one really simple method in there, which is send the message. This method takes the message as its parameter. The application cant say, where the message should be sent to, it doesn’t know what the destination is, doesn’t know what the endpoint is.
If someone did know that when they were writing code, it would change when the system got deployed. So there is no way in the world we can have our application level code dealing with destination of messages. So a bus, when we talk to it, send a message, and the bus will just handle where the message has to be sent to. So that’s the first thing. The other side of it is how we receive messages, or rather the way the application hooks into the bus when my autonomous service via this bus receives a message, what happes? Well if we just look at it at a purely logical level, a message is handled by a message handler. A message handler is a logical construct that embodies the service layer code of what to do with a message when it arrives.
So what we would like to do on the autonomous service side when it comes to receiving messages is to register a number of message handlers with the bus that in essence, we are saying to the bus – when this message arrives, dispatch it to this message handler, when a different kind of message arrives, dispatch it to a different kind of message handler. And that’s about it. We don’t have to use receive semantics or synchronous receive semantics, because the flow themselves are asynchronous. The fact is that we are bundling up a message, sending it off, that’s it! You don’t have to do all weird multithreading things within your message handlers that can deplete your thread pool and give you terrible performance and heavy loads and cause your service to crash. It really is very simple.
When you autonomous service hooks into the bus, it registers a bunch of message handlers and that’s about it. Once a bus receives a message, it just passes it along to a message handler.
There is one more special case that a bus should support and that is client side code. Because once the autonomous services use a bus to communicate with them, then end user application need to talk to the autonomous service using that same bus protocol.
Now request response is something that needs to be done when it comes to client side code. Client side code wants to say – Add this customer and it really needs to get a response back sometime. And it needs to be able to correlate the response message to the original request message. This is something else that the bus should do.
So we have identified two and half major areas that a bus is responsible for.
1. Sending a message
2. What to do when a message arrives – dispatch it to the message handler
3. How to deal with very basic request response semantics for client side code
Anything beyond that, we are already getting to workflow and workflow and persistent workflow and all those interesting stuff that happens when you go to route messages is totally out of the scope of the basic bus functions.
Now what is that a bus has to give us?
1. Send message when a message arrives
2. Dispatch it to the message handler
Now if we take a look at MSMQ or rendezvous or JMS, etc, it’s really rich, has a lot of power and you don’t really need it in the application level.
Back to the example of Add customer.
The client code as a result of user clicking OK on a forum would request the bus to send an ‘Add Customer’ message. And that ‘Add Customer’ message would be routed by the bus to the ‘Customer’ autonomous service, or maybe our ‘Sales’ service or maybe our ‘CRM’ service. That ‘CRM’ service would have bus take the message once it arrives and pass it to our ‘Add Customer’ message handler. That ‘Add Customer’ message handler would in some way or another talk to the database and save the customer details in the database, and would now like to send the data of the customer back to the client. Obviously the most important thing is getting that customer id back to the client.
So there are really two thing that we need to send back
Data – The customer id
That we were successful
This can be done is a number of ways, in this concrete example we’ll use one that I found very successful:
It is to send that same ‘Add Customer’ message back with an error code saying success. Your message was successful and this is the exact message you sent me and it was successfully handled. That is one message that the service would send back to the client. The client will resolve this and register success to the user and close the forum.
Now we still haven’t handled how the data gets back from the ‘Add Customer’ service to the client.
The nice thing about using a bus and this entire asynchronous messaging style of communication is that you can have more than one response messages, which is really great. So on the service side we got our ‘Add Customer’ message handler and after sending the above mentioned message, it sends back another message which is the ‘Customer Details Updated’ message, where it contains all the data about the customer.
The nice thing about this kind of message is that the customer service can decide that not only it wants to send the message back to the same client which sent the request, but it also wants to publish it, to all the clients that are interested in this message. So we get much richer semantics that are available once we go the asynchronous messaging route. We get a lot more capabilities of deciding what we want to say to who, when and have that all embodied in messaging.
So you can’t really do SOA or you can’t built loosely coupled autonomous services without having a bus between them.