I used to tell the curious that SOA's distinguishing characteristic was loose coupling. That definition probably says it all who those who already "get" it, but probably fails to communicate anything meaningful to those who don't already know what it is.
These days, I try and emphasise one particular kind of loose coupling that I think epitomises what SOA is, and I call it The Viewpoint Flip. The Viewpoint Flip is a more tangible concept than "loose coupling", and therefore more practical advice to give to aspiring SOA architects. The Viewpoint Flip is more important than breaking the synchronous request-response paradigm and replacing it with asynchronous messaging - that's transport sugar. The more important decoupling happens in the application domain, and I'll talk about it again in this post, although I've covered it in the past. A recent discussion with a colleague has convinced me that this aspect of SOA is often underappreciated even by SOA practitioners. That's why I'm talking about it once again.
The flip in viewpoint that I'm talking about is from the application domain's own navel-gazing view of itself to the view of an external party who wishes to interact with the domain for their own purposes. It's not for nothing that SOA is called "service-oriented". You can't be service-oriented, even in the English sense of the term, if you're focused on your own view of the world. You need to think about what
your customers want. Ironically, object-oriented systems (supposedly the pinnacle of modelling paradigms) may require the most wrenching viewpoint change, because their model is, by design, internally-focused. Older, procedural languages are often more naturally oriented to the viewpoint of an external party.
Two examples, one of which I've touched on before:
Take a drum (the musical instrument, not the vessel that stores water). Model its (State and) Behaviour using the OO paradigm. What methods can you think of that would model the drum's behaviour? If you said "beat()", you're still thinking procedurally. Beating is something
you do to the drum, not something that the drum does. What the drum does in response to the beating is make a sound. Therefore "makeSound()" is an appropriate method for the Drum class. That's how you would model the domain. (If you had a Drummer class, then "beat()" could be a legitimate method of that class.)
But "makeSound()" is not a service-oriented verb! It's not what a prospective external user of the drum would do. It's too internally-focused. Funnily enough, "beat()" is a good service-oriented verb. Procedural thinking is alive and well in the SOA world, it would seem.
Another example is the common one of funds transfer between bank accounts. For some reason, this simple example seems to tie everyone up in knots. I've seen examples like this:
fromAccount.transferTo( toAccount, amount )
or alternatively,
toAccount.transferFrom( fromAccount, amount )
Neither of them is aesthetically satisfying. Neither of them models the inherent symmetry of the "transfer funds" operation.
What we want is an unashamedly procedural syntax
transfer( fromAccount, toAccount, amount )
But we're too tied up in OO thinking to be able to do this elegantly. But that, if I may point out, is precisely what we need to be able to say in SOA-speak.
Insight 1: SOA thinking requires a fallback to procedural logic. This is one of the fundamental drivers for the Viewpoint Flip.
[The Domain Model for the banking Account class would probably have methods like "acceptDebit()" and "acceptCredit()". The "transfer" verb would be modelled from the start as a Service, if one goes by the principles of Domain-Driven Design, because "transfer()" (whether to or from) is not part of the behaviour of the Account class. It's this
non-Object-Oriented Service that would acquire the two account entities from a repository based on the identifiers passed to it, and then (transactionally) invoke the "acceptDebit()" method of one and the "acceptCredit()" method of the other.]
By the way, REST is beautifully elegant, partly because it achieves the Viewpoint Flip without anyone even noticing. GET, PUT, POST, DELETE - these are all things that
you do. They're not verbs that model the
behaviour of resources. They're verbs that model
what service consumers do to resources. When you adopt the REST model, you are unconsciously forced into service-oriented thinking and away from object-oriented thinking. With SOAP/WS-*, there's a degree of conceptual sophistication that's demanded of the designer, without which the whole thing degenerates into a bunch of remote procedure calls in XML syntax, needlessly complex and brittle beyond belief.
If I may fire my pet arrow at SOAP in the eternal REST-vs-SOAP debate, the approach of many Web Services tool vendors is to generate SOAP interfaces and WSDL files by annotating methods on objects. This is so naive I don't know whether to laugh or cry. There is no annotation in the world that can cause the "makeSound()" method on a Drum class to transform itself into the "beat()" service that should be exposed to external systems. The Viewpoint Flip is a sophisticated conceptual exercise that only a human designer can engage in. If you rely on tooling to expose services from your domain objects, and you end up with a SOAP operation called "makeSound()", that's not SOA, that's a horrible Distributed Objects train wreck. Distributed Objects don't work.
In contrast, so many good things come together thanks to the singular concept of the Viewpoint Flip.
RPC goes out of the window, because we're not doing remote method calls. We're fundamentally transforming the nature of the verb. So it can't be about taking a local method on a class and making it remotely invocable. People have railed against SOAP-RPC for years (and
I have as well), but the most damning argument against SOAP-RPC, I now realise, is that it doesn't implement the essential Viewpoint Flip that's required to turn a Domain Model into a Service Model.
Contract-First Design is another approach that emerges naturally out of accepting the need for the Viewpoint Flip. If you can't automatically generate a service interface from methods on objects, then it follows that it must be independently designed. In other words, the Service Interface and the Domain Model are independent, first-class entities. Neither can be generated from the other. They must be mapped to each other.
Did you notice that that's loose coupling? Change either the Domain Model or the Service Interface, and the other doesn't necessarily have to change. You may just have to change the mapping logic between them and hence preserve the other.
Nowadays, when someone asks me what SOA is all about, I don't glibly say "loose coupling." I say very slowly, "It's about being s-e-r-v-i-c-e o-r-i-e-n-t-e-d, being oriented to the viewpoint of the service consumer. SOA thinking means looking at a domain from the outside in, from the perspective of a consumer and what they wish to achieve, rather than how the domain sees itself, i.e., as a set of inter-related objects." [As
Kirstan observes, it's also the most natural way in which a business analyst looks at the domain, i.e., as a set of service "steps" that can be composed into a business process.]
A bit of philosophy: I read somewhere that when a student embarks on the study of Zen, the mountains are nothing more than mountains. Partway through the training, the mountains are no longer mountains. But finally, when the student has mastered Zen, the mountains are once again mountains, but they're somehow not the same as what they were.
So let it be with models of the world. From procedural thinking to object-orientation and back again to procedural thinking. But this time around, procedural thinking isn't quite the same thing. It's
service-oriented.