Service Oriented Applications (SOA) Application architecture (part 3)
In the two previous blog posts (one, two) I described the general SOA architecture and the kinds of messages we need to send around. Having messages is nice but we still need to define the message format. Part of this message format is a way for the central routing service to get request/response type messages around the system. So lets take a closer look at the messages.
The two most important considerations in message design are:
- Make messages chunky instead of chatty.
- Make messages state unaware.
Making messages chatty means that we send larger complete messages instead of small subsets. In general it is better to send a larger message and have all clients ignore part of it that send only the required subset and discover, at a later date, that part of the available information is missing from a message. So if a users enters a new invoice we are going to send a single message with the complete invoice details that we want the service to make public. Even if we are not going to use all the data in other parts of the application we should still send it. Remember that in a SOA application each service should be, as much as possible anyway, unaware of what others exist and what each does. Now suppose we know that there is no warehousing service only a financial service, this means we could decide not to send the articles sold only the customer and the total invoice amount. If the customer decides to implement a warehouse service at a later date the required information isn’t broadcast. Now we need to either add a new message or enhance the existing one. If we had decided to send the whole invoice in the first place we where already done at the invoicing end, all we had to do is add an endpoint for the invoice message.
Another thing is that we want to make them state unaware. This means we are not going to send a “new invoice” message but we are just going to send and invoice. This means that the receiving end needs to check, using primary keys etc, if the data is new or updated. The benefit is that it doesn’t matter if a message is received twice, something that may happen with a number of transmission protocols. In some cases this does put an extra burden on the receiving side as old state might need to be retained. Suppose the inventory receives a new order with a single article. As the order is new we can just mark a single item of the article as ordered. If the same order is received twice we can do one of two things:
- Realize that the same message has been received and processed and ignore it. Something that is easy to do if each message has a unique identifier, something I really think it should.
- Undo the previously reserved article and reserve it again.
The first of the two options is very easy to implement but also very limited. In most applications data that has been entered can be updated again so the scenario where the existing order is modified and a second article is added is quite common. Handling this case actually exactly the same as processing the same message twice and means that we first undo the previously reserved article and then reserve the two articles.
Of course the 1st option has some benefits two. As we are sure a single message is never processed twice we can send a diffgram style of message with both the old and the new value. Please note that diffgrams are useless if we can process the same message multiple times because the old value in the diffgram will not correspond to the last value used resulting in a wrong total.
So how does is message actually formed? Well a message is basically divided into two parts hold together by an envelop element. The first part of each message is a message header and is basically the same for each type of message. This message header contains information about the message itself. This includes information like:
- Originating service.
- Unique message identifier.
- Security information like a sender identifier or a way to make sure the message hasn’t been tampered with.
The second part is the message body. This is different for each message and is the actual payload that is processed in the receiving service.
The W3C consortium has published a standard format for these kind of messages; the WS-Addressing standard. See http://www.w3.org/2002/ws/addr/ for more details about this standard. Below is an example message taken from the WS-Addressing standard:
<?xmlversion="1.0"encoding="utf-8" ?>
<S:Envelopexmlns:S="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<S:Header>
<wsa:MessageID>http://example.com/6B29FC40-CA47-1067-B31D-00DD010662DA</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://example.com/business/client1</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://example.com/fabrikam/Purchasing</wsa:To>
<wsa:Action>http://example.com/fabrikam/SubmitPO</wsa:Action>
</S:Header>
<S:Body>
<!-- The actual message payload. -->
</S:Body>
</S:Envelope>
If you decide not to use the actual W3C standard I would recommend using something similar with at least the basic Envelop/Header/Body approach as this works very well.
A request/response message exchange is simply a combination of two messages. Take a look at the following example, again taken from the WS-Addressing standard guidelines.
The request message:
<?xmlversion="1.0"encoding="utf-8" ?>
<S:Envelopexmlns:S="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<S:Header>
<wsa:MessageID>http://example.com/someuniquestring</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://example.com/business/client1</wsa:Address>
</wsa:ReplyTo>
<wsa:To>mailto:fabrikam@example.com</wsa:To>
<wsa:Action>http://example.com/fabrikam/mail/Delete</wsa:Action>
</S:Header>
<S:Body>
<f:Deletexmlns:f="http://example.com/fabrikam">
<maxCount>42</maxCount>
</f:Delete>
</S:Body>
</S:Envelope>
The associated response message:
<?xmlversion="1.0"encoding="utf-8" ?>
<S:Envelope
xmlns:S="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<S:Header>
<wsa:MessageID>http://example.com/someotheruniquestring</wsa:MessageID>
<wsa:RelatesTo>http://example.com/someuniquestring</wsa:RelatesTo>
<wsa:To>http://example.com/business/client1</wsa:To>
<wsa:Action>http://example.com/fabrikam/mail/DeleteAck</wsa:Action>
</S:Header>
<S:Body>
<f:DeleteAckxmlns:f="http://example.com/fabrikam"/>
</S:Body>
</S:Envelope>
Using the general guidelines mentioned above and in the two previous blog posts you should be able to get good start with developing your first scalable SOA application. Good luck and let me know if you have any questions.