September 25, 2003
@ 03:27 PM

If you are a developer and don't live in the Netherlands (where SOA stands, well known, for "Sexueel Overdraagbare Aandoeningen" = "Sexually transmitted diseases”), you may have heard by now that SOA stands for "service oriented architectures".

What's really interesting about talks and articles about SOA (including the ones that I gave on this year's Microsoft Architect's Tour) tend to focus almost exclusively on the glue between services and how the use of registries and dynamic binding, message and service contract exchange and negotiation and use of standard protocols and data exchange formats promises greater flexibility for enterprise architectures, but little is said about the characteristics of the "services" themselves.

So, here's a bit of my current thinking around services; by now I think I could probably fill a book on the topic and therefore a blog entry cannot come even close to give the complete picture. Also, I don’t claim to say anything new here, but rather just want to have it all once in one place on my blog. So here we go:


Very broadly speaking, a service is an autonomous unit that is responsible for a transformation, storage and/or retrieval of data. Services never interact with other services by side-effect, meaning there is no notion of inter-service (application) state that is not explicitly exchanged through messages. Services are accessed through well-defined public access points that are governed by contracts that tightly define the set of supported messages, the message content and the applicable service policies.

To explain services and their motivation, I will first have to write about objects. The basic idea of exclusive data ownership is not too dissimilar to the idealistic view domain objects. There, you have an object “Customer 12345678 Peter Miller” and that object has its own “save data” and “load data” capability. To activate (load) an object from persistent storage, you go through some sort of factory that is getting the object identity as an argument and from there, all you talk to is the object and the object’s inner implementation worries about the details of storage all by itself.

However, in contrast to the object notion of having data and code in one place and at one location, services strictly separate between code and data. The customer record mentioned above isn’t a uniquely identifiable in-memory and even not an addressable on-disk entity that’s known throughout the system, but data simply flows through the system and the same record may exist in multiple places at the same time. In a service world, there are no objects, there is just data.

The idea of “self-contained” domain objects, while thought to be an ideal modularization model, indeed most often fails to provide modularity. A data record can be stored to and retrieved from many different data sources within the same application out of a variety of motivations. It may be stored in an offline “isolated storage” replica on a mobile machine, inside a queue message that is processed only during an hourly or daily batch run, in a SQL database, an in-memory caching structure and many more places based on its character. The character of a data record includes, for instance, how often and by how many concurrent activities it is likely to be changed and therefore how safe it is to create read-only replicas of the record and how long these replicas can be regarded as being valid and accurate or even just “good enough” to base further processing on them. Likewise, a data record can be rendered for presentation to both human and machine consumers is a vast variety of ways, ranging from an XML fragment over HTML rendering to sophisticated 3D graphics visualizations.

Although the idea of object-centric storage, object self-responsibility and universal object-identity is fantastically attractive, a single object implementation that attempts to accommodate all these requirements simply results in a monolithic application block that is anything but modular. Even when it comes to “business logic”, the implementation of the rules that govern the contextual correctness and integrity of an object, putting all rules that result from the requirements for an entire system into a single class breaks the separation of subsystems. Creating a single “customer” object class for a bank’s loan, investment and financial collections business is essentially impossible because of conflicting rules and requirements and a different perception of “customer” by these businesses. Still, it is standard procedure to have a central database with data records that hold the customer information shared by these systems – and a service that governs this data store. Not too rarely that service goes by the name “host communication” and shifts data records on and off the mainframe via CICS transactions.

The consequence from this thinking about domain objects and generally about the notion of object identity and self-responsibility (and I am sure that a lot of people will disagree violently with me on that) is that there is not only no proper way of realizing the dream of “true objects”, but there is indeed no way of defining any method for a domain object in a way that it doesn’t result in a monolith spanning multiple concerns having methods that are inappropriate or wrong to be used in certain contexts.

However, this statement explicitly excludes property access methods that enforce rules like “value must be greater-equal to 0 and less-equal to 100”, because the value in question may represent an expression in percent. Now, one could argue that the fact that such property access methods are a clear example why domain objects do indeed make sense, because these method implement fundamental business logic, but in my view they don’t. The fact that property access methods enforcing such rules must exist, simply fixes an inherent weakness in the type system of most mainstream programming languages. The rule [0<=x<=100] is a property of the “percentage” data type, but that doesn’t readily map into most languages. Hence, it’s the job of explicit coding to fix that limitation and provide stronger types. The type description language XML Schema (and siblings like Schematron and Relax NG) provides facilities to define data types of the desired strength and infrastructures supporting these description formats are capable of either enforcing these rules without specific coding or generating the code required to enforce them. Property access methods are just a way to overcome programming model limitations and enforcing contract, they are not an object feature or business logic. At least I don’t see them that way.

So, if you’re still reading after I’ve slaughtered the idea of “objects” for a modular, layered and even distributed system, it’s not so far to go from here to the essence of what a service is.

To recap the initial statement, a service is an autonomous unit. The autonomous character of a service results from the combination of exclusive responsibility for certain operations on data and a strict definition of the message contracts for both the messages it receives and the messages it is able to provide.

Exclusive responsibility means that there is exactly on service in a given system that may perform a certain operation on data, for instance storing and retrieving data into a certain set of tables on a certain database. Any other service that requires access to this data must use the responsible service. This serves to guarantee that only a single implementation of (for instance) data consistency rules exists, but also helps to eliminate assumptions that hinder (again, for instance) scalability. One of these problematic assumptions is that all records of a given type are co-located in the same database or in the same location. That assumption is okay as long as you don’t have to deal with a massive data volume or very high concurrency with very frequent transactional writes. In these cases, it may be beneficial to break up the storage into multiple tables or even across multiple databases, which may or may not be directly supported by your database system. If it isn’t or doesn’t work with the desired flexibility, it’s nearly impossible to introduce this scalability technique once everyone is permitted to access backend storage directly. (To get an idea of this sort of parallelism and partitioning, check out this PPT by Jim Gray).

Ruling out that state is implicitly shared between services (in memory or on disk) is a direct consequence from this and also serves the scalability purpose, because it further eliminates co-location assumptions about services and enables clustering. Note that this isn’t about “stateless” or “stateful”. Everything is stateful while it runs.

Strong contracts and operational guarantees further allow you to rely on (trust) the service that it will be able to perform a given task without passing the caller an error that it likely can’t handle, anyways. If the message contract and the description of types are sufficiently precise, a service won’t ever and should never have to come back to the caller with an “invalid argument” exception. If input is compliant with a contract, it’s the receiving service’s own problem to deal with any issues it has with the data, even if that involves manual resolution by an operator. The sender (client) can’t and won’t have any additional information and implemented measures to fix the input if the contract isn’t sufficiently expressing the constraints. Operational guarantees like transactional processing and reliable transport make sure that the data that is passed on to a service does not get lost on the way or gets lost when a processing attempt is unsuccessfully. If a service (A) can trust that an invoked service (B) will be able to handle a set of data contained in a message and can trust that processing will occur without further intervention by (A), the processing can occur asynchronously and the message sent from (A) to (B) can be queued and load balanced.

This is not only true for one-way storage operations, but also for requesting data. If (A) can trust that service (B) will not simply fail a request operation and die, but is able to recover from any problem (with a reasonable probability) it may run into and send a response, and (A) passes (B) a reply-to entry point to drop the request result into, (A) can safely trust to end or suspend processing until that reply arrives or, if required, a timeout occurs. This type of asynchronous “call me back when you’re ready” interaction between services is called “dialog” and much better suited for fair load distribution in distributed systems than request/response. In essence, dialogs turn the call trees resulting from request/response operations into a sequence of one-way operations. [A further important aspect in this context is 2PC vs. compensating transactions, but I won’t go into that here and now]

Asynchronous and parallel operation is a key element of both scalable systems and systems that operate well in the presence of substantial communication constraints like network latency and the required processing introduced by strong security boundaries. The vision of Web services as an integration tool of global scale exhibits these and other constraints, making it necessary to enable asynchronous behavior and parallel processing as a core principle of mainstream application design and don’t leave that as a specialty to the high-performance and super-computing space.

Summarizing, services and service oriented architectures are, in a sense, a return to quite a few of the good old principles of structured programming and batch processing. Data and code are kept separate in order to allow cross-organization, cross-platform modularization, and asynchronous processing is better than synchronous processing if you want your systems to scale. But service oriented architectures also mean that we rely much more on the abstraction and tighter definition that data contracts provide compared to what can be expressed in a programming model. Message contracts expressed in a rich, cross-platform type description language such as XML Schema are much more powerful and precise than any IDL file you could ever write and they are independent of the implementation platform that’s chosen for a particular subsystem. Service policy contracts provide a similar abstraction for the operational requirements and guarantees that can be mandated or given in order to establish the required level of trust between services independent of the platform they are implemented on.

[For some answers to reader comments go here]

Sunday, September 28, 2003 2:49:09 PM UTC
Please give us a 'Print this' option. Currently it is horrible, not to say impossible, to print out your excellent articles and postings.
Monday, September 29, 2003 12:02:43 AM UTC
Hi, Clemens. Thought provoking article. I have some questions and concerns, however. I'll try to keep them brief, by paraphrasing one of your claims, and then elaborating my postion. Please excuse my ignorance. :-)

1) "No shared application state, everything must be passed through messages."

Every "service" oriented system I have ever witnessed has stated this as a goal, and eventually someone got sick of it and implemented a form of shared state. The GIT in COM, session variables in PL/SQL packages, ASP[.NET] Sessions, JSP HttpSession, common areas in CICS, Linda/JavaSpaces, Stateful Session Beans, Scratchpads / Blackboards, etc.

Concern: No distributed computing paradigm has ever eliminated transient shared state, no matter how messy or unscalable it is.

2) "A customer record isn't uniquely identifiable in-memory and even not an addressable on-disk entity that's known throughout the system"

Question: This confuses me quite a bit. Are you advocating the abolishment of a primary key for a piece of shared data? If not, what do you mean by this: no notion of global object identity (fair), or something else?

3) "In a services world, there are no objects, just data".

Oh, I wouldn't be so hard on objects. It seems to me that objects are primarily about encapsulation and polymorphism. Global object identity seems to be getting a bit too much attention from you in this entry: I don't think it's very prevalent anymore (even EJB entity beans, bless their ugly little souls, don't use OIDs, they use primary keys based on type.)

Anyway, I don't think anyone [sane] has advocated building fine-grained object model distributed systems for quite a few years. I think your "slaughtering of objects" is rather unwarranted. You can't build a modular system out of a "One True Definition" of domain objects, that's fair. But the object oriented community has known that for quite some time, hence the "Facade" pattern, and the packaging/reuse principles from folks such as Robert C. Martin. Domain models may still exist in the implementation of the service, depending on the complexity of the service.

From one perspective, SOA is about XML documents acting as the facades to either procedural logic (for simple services) or domain models (for complex services). This isn't the only way to look at it, but I think it's a plausable approach. I'd be interested to hear if/why domain models still aren't appropriate for implementing complex services.

4) "data record stored & retrieved from many different data sources within the same application out of a variety of motivations"

I assume all of these copies of data are read-only, with one service having responsibility for updates. I also assume you mean that some form of optimistic conflict checking would be involved to ensure no lost updates.

Concern: Traditionally we have had serializable transaction isolation to protect us from concurrent anomalies. Will we still have this sort of isolation in the face of multiple cached copies across web services?

If not, to avoid semantic constraint errors, all potential update operations must to do an optimistic conflict check on all data involved in making an update decision, even if that data isn't actually being updated. This increases the probability of retries because the surface-area of your update will be much larger, and lots of retries = lots of wasted processing.

5) "there is indeed no way of defining any method for a domain object in a way that it doesn’t result in a monolith spanning multiple concerns having methods that are inappropriate or wrong to be used in certain contexts. "

Perhaps in naive domain models, but I honestly haven't seen this trouble in sophisticated ones with notions of dynamic roles and contexts. Anyway, I digress....

6) "Exclusive responsibility means that there is exactly on service in a given system that may perform a certain operation on data, for instance storing and retrieving data into a certain set of tables on a certain database."

So, a web service is basically an XML interface to a stored procedure package?

7) "Problematic assumptions regarding single databases vs. parallel databases for scalability"

I'm not sure what the problem is here from an SOA perspective? Isn't this a physical data architecture issue, something encapsulated by your database's interface?

As far as I know it's pretty transparent to me if Oracle decides to use a parallel query, unless I dig into the SQL plan. Similarly if I pass an update statement to an Oracle RAC cluster, it will pick a node and I won't really know which one it's hitting. In SQL Server's shared nothing approach this might be a bit different but I think those distributed views have some notion of knowing which machine to hit for what subset of data...

8 ) "Strong contracts eliminate "illegal argument" errors"

Question: What about semantic constraints? Or referential integrity constraints?

XML Schemas are richer than IDL, but they still don't capture rich semantic constraints (i.e. "book a room in this hotel, ensuring there are no overlapping reservations" -- or "employee reporting relationships must be hierarchical").

Concern: I fail to see how strong XML contracts will eliminate "illegal argument" errors that are a result of bad semantic pre-conditions on the client side. It will eliminate simple syntactic or formatting errors like numeric ranges, regular expressions, or referential integrity within a single document.. but that's a fairly small piece of the puzzle. Am I missing something?

9) "The vision of Web services as an integration tool of global scale exhibits these and other constraints, making it necessary to enable asynchronous behavior and parallel processing as a core principle of mainstream application design and don’t leave that as a specialty to the high-performance and super-computing space."

Concern: Distributed/concurrent/parallel computing is hard. I haven't seen much evidence that SOA/ web services makes this any easier. It makes contracts easier, and distributing data types easier. But it's up to the programming model (.NET, J2EE, or something else) to make the distributed/concurrent/parallel model easier. There are some signs of improvement here, but I'm skeptical there will be anything that breaks this stuff into the "mainstream" (I guess it depends on what one defines as mainstream)...

While SOA as a means of widespread systems integration is a solid idea, the dream of service-oriented "grid" computing isn't really economically viable unless the computation is very expensive. Co-locating processing & filtering as close as possible to the data source is still the key principle to an economic & performing system. (Jim Gray also has a recent paper on this on his website). Things like XQuery for integration and data federations (service oriented or not) still don't seem economically plausible until distributed query processors get a lot smarter and WAN costs go down.
Friday, November 07, 2003 11:29:48 AM UTC
Clemens and Stu:

I like your discussion, I would like to add my two cents. I think what SOA is telling is that the notion of Business Object and/or Distributed object is not the right one. Let me explain my position.

Typically, a "business object" such as a PO or a Patient Record or a Part definition, would flow through lots of different processes over its lifecycle (creation -> archiving). Some BO lifecycle can last weeks, months, years, decades, sometimes outlast the system that created them.

The business object data can be modeled anyway you want, ER, Object, XML, ... what I think is wrong is to associate application state (or business process state) and the corresponding business logic to change state to the business object itself (e.g. process is created, approved, paid, archived). However, if you get read of this kind of thing in the BO definition, you kind of lose the benefit (if there were any in this case) of Object Orientation altogether. A data object is not really an object, it could be mapped isomorphically to XML or ER. Note that is equally wrong to store this state within the same tables of BO if you use an ER model. XML is the only flexible model that would let you "add some state" without interfering with the object and vice versa.

So what a service oriented archiecture let us do is basically favor this separation of state and data. Your data is free to flow through various services that may update the application (or business process) state, even marshalling as XML around your data to pass it to the next service. This approach is necessary as soon as the number of business processes and services become large. Imagine a patient record (1800 tables), a patient record is going to go through lots of processes, I think that it is not a good idea to manage the state of the patient at the same level as its record. I view SOA as the architecture needed for "highly/massively connected systems".

What this tells me though is that the state is going to be managed within the web service fabric or the glue as you call it clemens. Concepts like: composition, orchestration, choreography, coordination, collaboration, protocols and interactions are going to be key to establish an SOA application model. This is also precisely because distribute/concurrent/parallel computing is hard that you need this kind of glue (as semantic declarative languages - imagine coding this in Java?)

Overall I think that SOA (and highly connected systems) is questioning pretty much all the beliefs of software engineering:
Object -> Continuum object-document
Producer oriented contracts -> Consumer oriented data exchange
Integration -> Collaboration/Coordination and Federation
Synch RPC calls -> Asynch document oriented messaging
Distributed objects -> services and messages
Procedural and syntactic languages -> semantic and declarative languages
The only thing that will stand still, I think, is MVC.

Please check this presentation that I am giving at a conference at the end of the months that details all this.

Monday, October 18, 2004 9:02:05 AM UTC
I have seen and worked on monolythic object oriented systems and your post resonates to a degree with me. I have also learned that in many instances, it was my fault.

With respect of your statement where you claim having slaughtered the idea of a "modular, layered and even distributed system", I would like to hear your ideas on Croquet:
Wolfgang Baeck
Monday, June 05, 2006 1:23:25 AM UTC
wow !
Monday, August 14, 2006 7:04:49 PM UTC
I was populous, of course!
Paul Kroh Battle is as bumptious as a coward.
My surreptitious copiousness in the blind caves her purplest nasality. Falsest Matt Russell quick Rolf the peeling and aesthetically slope the hoarser interrelation.
Those extraneous, posthumous, organic commemorators of summer!
I reprobate some disguisements, I redouble and debouch, I go to the liter.
The hot glumness abbreviates wearily.
Plutocrat headline fallaciously hollow, autocratic intermatch his omnipresent flocculence.
I don't care about Emad Alabsi, he is allegorical, anarchic, and lankiest and I am not going to detail about it.
Hey Robert Doyle, don't be circular.
Comments are closed.