Friday, February 22, 2008

The Deciding Characteristic of SOA - The Viewpoint Flip

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.

11 comments:

David said...

Nicely put and thanks for repeating things for newer readers. Thinking of this concept in a different setting, I would contrast Apple with other mp3 player makers by saying that the iPod is designed with a service-oriented viewpoint flip whereas so many others can never quite get it right because the design is driven by the underlying technology. If you added a couple of simple sketches to your piece I think it would make an excellent piece for a magazine.

Kirstan Vandersluis said...

Ganesh, best explanation I've seen about why you can't just auto-generate services from your classes.

I do believe the need to shift viewpoints emanates from the core goal of SOA: the ability to orchestrate new processes from existing services. Now, who creates a new process? It should be a business analyst, and the process uses individual steps aligned closely with services. In fact, these steps should be the very definition of the services. So, "think like a business analyst" is another good motto for service designers to help focus on the needs of the service consumer.

Ganesh Prasad said...

Kirstan,

Thanks for your comment. I've taken the liberty of adding your insight to my new definition of SOA (third-last para).

Regards,
Ganesh

Kirstan Vandersluis said...

Thanks Ganesh, I'm happy to help in some small way. Keep up the great blog-work! I find your posts both entertaining and enlightening!

Kalpesh said...

Nice explanation. Yes, we are back to procedural world - taking some of the goodness of OO.

In case of bank transfer, this is how I see it in terms of steps (assuming I am given a check by someone to be deposited in my account)

1) I goto bank & give them the check with a deposit slip

2) Bank verifies it & then sends it to clearing house

3) It goes to other bank, having the other person's account. The bank verifies the signature, balance etc

4) If everything is ok, bank transfers money to central bank (RBI in case of India)

5) RBI then sends money to my bank, which in turn puts it into my account.

Yes, there could be lots of exceptions to this & modeling this into SOA style would be fun
e.g. Invalid check, Balance insufficient

And it makes me think other effect.
i.e. if the balance is insufficient in the account (of person giving me the check), it would cause the dishonour of check & hence bank will charge me & him the fees :)

And, everything will be a single transaction (either success or failure). The response could be failure or success (with money in my account).

Isn't it funny that we take things for granted or it has become so transparent that how many things go under the hood in a process so simple for us to see?

Thanks.

David said...

It's a bit off topic, but Kalpesh's comment reminded me of a thought I've had.

"God thinking" (where one thinks on behalf of multiple people) also can be known as universal thinking (these are just my terms) has to be a pretty recent development for humans. I can imagine a particularly clever cavewoman considering how the hunters should corner an animal and considering issues from multiple viewpoints. However I don't think this is a natural thing for us to do because of our necessarily subjective nature.

So how do you expect to feel comfortable whilst exactly and perfectly specifying the interactions between service providers (or objects) as required by the "deposit cheque" example.

Agile / Extreme uses a kind of game or negotiation I think to resolve some issues and I'm thinking that the best way to determine service interfaces is to either copy them exactly from real life (perhaps not even knowing why they are as they are) or do what real life objects have previously done - which is consider their own needs and negotiate with others.

The easiest way to think this kind of problem through may be to simulate it using several humans, each playing the part of an entity. They should consider their needs, their resource usage, their profit, their opportunities etc and negotiate accordingly.

This may be obvious to some, or absurd to others. But from what I'm learning at the moment about human thinking, the more I'm coming to think that "God thinking" is a new activity, we're not good at it yet and probably never will be.

Udi said...

While I agree that trying to "scale-up" object-oriented thinking to SOA will probably lead to a less than optimal solution (and even put up a podcast on the topic), I'm not convinced that a procedural mindset will help. I'm actually quite certain that will lead to merely larger scale disasters than those we witnessed when trying to scale procedural programming to larger problem domains.

Just recently I posted how services in SOA would be highly correlated to loosely coupled business domains.

Those services might expose endpoints accepting messages similar to those you described, yet services can publish messages too notifying interested parties about changes to their state.

Anyway, glad to see more people picking up the anti-OO SOA drum :)

Ganesh Prasad said...

Udi,

When I said we need a procedural mindset for SOA, I also qualified it with the Zen analogy "the mountains are once again mountains, but somehow they're not the same as they were before". In a later blog entry, I have explained where the SOA approach needs to be procedural, and where it stops being so.

I like your statement about services needing to be demarcated based on "business fracture lines". Context diagrams may help to tease out those boundaries.

Regards,
Ganesh

Jesse said...

I'm not grasping everything that was said in this article or in the comments, but I was wondering what your response to a transferAgent object would be?

transferAgent.transfer( fromAccount, toAccount, amount )

Ganesh Prasad said...

In Domain-Driven Design terminology, your TransferAgent class is not an Entity but a Service. In other words, you're now applying SOA thinking even inside your predominantly OO domain and have implemented the Viewpoint Flip. If you've done that, you could auto-generate your service interface from your Service classes, because that would be a straightforward case of remoting alone.

Regards,
Ganesh

Jesse said...

Thanks,

Is a service oriented object really at odds with OO design?

This would be a sort of facade, but I imagine it would also be extremely useful even within the program.

Why would an object be able to transferTo or transferFrom another object? Shouldn't objects be primarily concerned with themselves? I would expect an account to be able to increaseFunds(), decreaseFunds(), and perhaps recallBalanceChangeDetails(), registerBalanceChangeDetails()