I was perusing Stefan Tilkov’s entry “10 Principles of SOA only to be stumped with the first principle. His first principle talks about “Explicit Boundaries”, the principle comes’ from Don Box’s “Four Tenets” desribing Microsoft Indigo framework. What stumped me was this sentence:
A service invocation should xe2x80x93 as a general pattern xe2x80x93 not rely on a shared context; instead service invocations should be modeled as stateless.
To be fair, I’ve made a similar statement in my defintion of services where I write:
No implicit state sharing between Services. Unlike Objects or Components, the Semantics of an invocation will be derived from information available in the incoming invocation and its internal state. The idea is to support complete isolation between services, there should be no intergalactic wormhole type signaling.
The use of the word “stateless” can be quite confusing, and the phrase “invocations should be modeled as stateless” needs better elaboration. It’s obvious that Services can’t be stateless, otherwise they would be nothing more than distributed pure functions. Which incidentally only makes sense if the cost of computation is exceedingly large and therefore needs to be outsourced to some kick ass server farm. (Incidentally Hadoop or MapReduce which are based on functional forms aren’t stateless.)
What really stumps me is how to achieve not having a shared context. Pronouns exists in language so that sentences when composed relate to each other. The act of communicating a reference to an object establishes a shared context. The absence of any reference passing implies there are no other objects involved other than those in the ownership of the two objects in conversation.
That then is the essence of the idea of “explicit boundaries”, that is if there is state to be shared, it is explicitly known by the services involved in the conversation. If the state is owned by any of the services in the conversation, then the access to that state is possible only through the interfaces provided by the owning service.
State sharing therefore is allowed only if reference passing is made explicit. For this to work there needs to be prior agreement on what a reference looks like. Fortunately the web has its pre-established conventions (i.e. the URL). One can of course always cheat by tunneling state references in a vocabulary known only by the services, that of course tends to increases coupling considerably.
The point however of Services is that we want to avoid the mistakes of database development and object oriented development that came before. What mistakes where these?
The problem with database development was you were never aware if data underneath your code changes by some other piece of code. In an environment where there’s a global shared database with a bunch of random scripts, one can never be sure if one can enforce the proper semantics for every state access.
Object Oriented development cured consistency problem, however introduced yet another problem. Encapsulated state has the additional benefit of simplifying how data is accessed. No longer are we burdened with the need to intimately know the schema to phrase queries to get answers. We could now call simplified predefined methods that made it easy to get answers but also ensured semantic integrity. However, queries don’t stand in isolation, they come in groups. This lead to a style where object queries are followed by more contextually related object queries. Object oriented conversations followed a form where we would navigate and iterate our way through our models.
Object oriented conversations are inherently stateful. The power of encapsulated state is a double edged sword. One can easily be lured by the appeal of patterns like the Iterator, where the result of each use of method call depends on the method that was perviously called. The Iterator is actually a specialization of the more general pattern of Curried Object. The Curried Object pattern in essence encapsulates object interation providing the client with a much simpler interface.
A Curried Object stores the constant or slowly varying arguments from the original protocol, and
provides a simpler protocol with these arguments eliminated. A Curried Object stores the original
server object, and forwards messages to this object, passing along the stored arguments and updating
the slowly varying arguments.
It is this kind of interaction that is eschewed by distributed systems. The number of method calls leads to too many network calls (see Transfer Object). But it is more than an issue of efficiency the it is eschewed. It is also an issue of composability.
It is already complicated to build transformation mappings between two structures, however its a whole other order of complexity to transform one form of interaction into another. Thatis when you introduce timing constraints in the equation, then as far as I’m concerned, all bets are off if you contemplate building an automated procedure.
To summarize, shared context should be fine so long as that sharing is explicit. Services aren’t stateless, however we want to minimize the stateful coupling between method invocation. I guess that’s called ‘stateless invocation’ for lack of a better term.