Comparing S-O and O-O as design principles and not as implementation technologies

Michi Henning made some very interesting comments on my "Loose-coupling through the relaxation of endpoint assumptions" post. Before going into the specifics of Michi's comments, please allow me to emphasize that nothing from what I have been arguing is about CORBA or DCOM or any other distributed objects technology. I am just trying to identify the differences (if any) between service-orientation and object-orientation as design principles, as conceptual architectures for applications rather than as implementation technologies. One could use a distributed-objects platform to build service-oriented applications (e.g. CORBA folks have been doing this for some time) or use a platform that is more suitable for applying the principles of service-orientation in order to build object-oriented applications (e.g. the WS-RF folks).

Now, onto the specifics of Michi's comments...

The first point that Michi makes is about URIs. He uses my example to illustrate how one could infer resource identity from a URI. I think my point was misunderstood there. I was talking about the coupling of transport specific information and resource identity and how decoupling the two through an indirection may further enable loose-coupling. One should not break a URI apart, as Michi does in his example in order to infer resource identity unless there is a URI scheme in place with widely accepted semantics (e.g.  uuid'). For example, we don't know whether ftp://example.org/resource1 and http://example.org/resource1 point to the same resource. We should not assume that "resource1" is the identity of the same resource irrespective of which protocol is used. What about ftp://example.org/pub/resource1? Or, what if the same resource was available at a different endpoint (e.g. http://example2.org/resource1)? Can we break the URI apart in order to infer resource identity?

We just don't know unless we put additional infrastructure in place to allow for comparisons of URIs. My point was that all these are protocol-specific addresses and not identifiers of entities. Decoupling identity from addressing is a good thing. Building distributed systems using identities rather than addresses is also a good thing for loose-coupling even though this introduces a level of indirection. REST promotes the use of URIs like the ones in my examples that are used both as identifiers and addresses. It is also the case that object-orientation as a design principle promotes the use of addresses in interactions since we pass references and not names or identifiers for type-safety reasons. If someone using CORBA decides to use an object identifier instead of a reference, then this is an indication that they are sacrificing pure object-orientation in order to introduce indirection in an attempt to increase loose-coupling.

Then, Michi comments on the issue of object interfaces vs service contracts for messages. I see two differences.

  1. In traditional object-oriented systems, an object is always coupled with a specific interface while in service-oriented systems a service may support multiple contracts/policies. Please remember that we are talking about design and not implementation platforms like CORBA. Yes, I know that a CORBA ORB can receive IIOP messages on behalf of multiple objects. But that's an implementation detail and in the overwhelming majority of cases is hidden from the developers. I am talking about how we design applications and not how we implement them. In object-oriented design we see objects and method invocations while in service-oriented design we see services and messages.
  2. In pure object-oriented design, interfaces are the means for building type-safe systems; in service-orientation we describe message formats. Loose-coupling comes from the fact that we are more flexible in how we describe the structure of messages (e.g. we can define element ordering, cardinality of elements, etc.). In pure object-oriented design everything has to be type-safe and this introduces tight-coupling. It is also the case that behaviour is defined in terms of methods which act on objects. In service-oriented design we advocate for application behaviour to be associated with message exchanges and protocols.

Afterwards, Michi suggests that object state is associated with an object identifier. Yes, I agree. However, for type-safety reasons pure object-orientation means sending an object address (a reference) around when referring to the state and not an object identifier. Object identifiers are means of indirection and, hence, a way to relax the assumptions about the endpoint (the logical endpoint) at which an object is attached on the network. Again, I have to repeat that I am talking about how a system is architected and not how it is implemented. The fact that there are application servers that can manage the state of an object across multiple computers doesn't change the fact that a different part of the distributed application was bound directly to that state at the design phase.

As far as having "stateless objects", I have to point to what I was taught...

"An object has state, behaviour, and identity; the structure and behaviour of similar objects are defined in their common class; the terms instance and object are interchangeable" [Booch, Grady. Object-Oriented Design with Applications, 91]

I know that there are many other definitions too that don't use the word "state" but to my mind the one above best captures the essence of an object. This relates to Michi's next argument...

"But that is splitting hairs. For one thing, if I make a stateless CORBA bank object that accepts that same identifier for the account, I have done the exact same thing you do in your your WS example. But to say that, somehow, this is talking "about" the resource (and that there is any architectural difference because of this) is simply naive. Face it: if a customer wants to do something with a bank account, the customer has to identify the account. Whether that happens via a separate parameter that contains the identity of the account, or whether that is done by hiding the identity inside a URI or object reference is completely irrelevant." [Michi Henning, comment]

I think it makes all the difference if you use a name or an identifier to specify the account instead of using an address. If you use a name/identifier, then you are introducing an indirection at the cost of type-safety and this is contrary to the principles of pure object-oriented design. People do it because they want to avoid the tight-coupling to which strict type-safety may lead.

And then on to specific points...

"1. The handles we use to identify addressable entities (URIs or IORs) contain a protocol specifier."

And I suggested that we shouldn't couple the identity of entities with protocol-specific addresses.

"2. The handles we use to identify addressable entities (URIs or IORs) contain an endpoint identifier (such as a domain name)."

Well, not necessarily. I used examples such as creditcard:mastercard:123-123-123-123. No communication-specific information was encapsulated in this identifier although it's still a URI.

"3. The handles we use to identify addressable entities (URIs or IORs) contain an identity (service name, path in the local filesystem, or object identity)."

And my point was that using such identifiers for our entities is a bad thing. Do these URIs point to the same resource? http://example.org/resource1, ftp://example.org/resource1, http://example2.org/resource1? We just don't know. However, there is no ambiguity about this identity: bank:barclays:account:111-222:333-444-555-666 which does not imply any communication-specific address.

"4. Both approaches have an interface. In case of WS, the interface consists of the <bank:MoneyTransfer> message type (with an agreed-upon number of subnodes), in case of distributed objects, the interface is written down using something like IDL. Either way, both approaches specify the same information."

The difference I showed was that in one case the interface to the bank accepted references to objects while in the WS case it accepted names. I must admit that from simple examples like the one I showed is very difficult to demonstrate the issues because people can very easily treat a message as a method invocation request. However, this is not the case. A message in service-orientation is seen as the transfer of information between two services and not as an encoding of a request to dispatch a method.

"5. Both approaches exchange the same information over the wire: the identity of the accounts involved, and the amount of money to be transferred."

As I said above, in one case we passed network addresses (the references) and in another names/identities that had to be dereferenced (the indirection).

"6. Sending a message that is unexpected or is internally malformed causes an error with both approaches."

Yes, if the contracts and policies that are in place are not adhered, then there shouldn't be an expectation that the message would be processed by the service. That's not to say that it won't. In fact, MEST says that the message will be processed but it's very likely that it will be rejected from the first layers of message validation.

"7. Both approaches transfer money from one account to another, and the accounts remember that this has happend. Therefore, both approaches are equally stateful. (After all, by their very nature, accounts are stateful. How could we possibly have an account that is not stateful?)"

No argument there. However, with the SOAP message I tried to show that the accounts were not directly addressed; they were just named. The application never bound to the actual state. Of course type safety was lost since we are now passing a string name and not a reference to a typed entity. We are asking the service to do more in order to interpret the name/identity given to it as a string. If one creates a CORBA "object" in this manner, then they are applying the principles of service-oriented using an object-based implementation platform.

Michi concludes his comments with

"I really wish people would stop going on about the architectural differences and between WS and distributed objects, and loose coupling. There are no differences, and neither system is more loosely coupled than the other. At an architectural level, they are equivalent."

I have to point out that I am not talking about Web Services vs distributed objects platform like CORBA but about service-orientation vs object-orientation as design principles for architecting applications. I feel that the WS platform is a good candidate for building service-oriented applications but CORBA can be used as well. Similarly, Web Services could be used to implement object-oriented applications.

CORBA is not the point of the discussion here. It's how people design applications. It has certainly been the case that many CORBA folks have been aware of the issues related to loose-coupling and they have designed and built systems accordingly applying most (if not all) the stuff I am talking about (I know that I am not saying anything new). However, this is not to say that there is not a difference between object-oriented vs service-oriented architectures (not the implementation platforms).

It's all about designing for loose-coupling (I think Mark Little used those words in one of our conversations) and then choosing the right tools. My starting point is the architecture and not the tools and I personally see a difference between S-O and O-O at the architectural level.

6 responses to “Comparing S-O and O-O as design principles and not as implementation technologies”

  1. I think saying that “an object is always coupled with a specific interface” being unnecessarily restrictive? Aren’t you forgetting about the venerable QueryInterface() and its ilk, whose job is to specifically to allow a single object to implement and expose multiple interfaces? QI is most definitely NOT hidden from COM developers – it’s fundamental to COM programming. I think a major difference between SO and OO is the relationship between data (state) and behavior. Traditional OO binds some data and some beavior into a single conceptual unit (the instance). SO systems keep data (messages) separate from behavior (services). By keeping the data contract independent from the behavioral contract, the two are allowed to vary independently. Because data is separate from behavior, there’s no concept of “instance identity” in SO systems. That, I think, is a big difference between the two approaches.
  2. Hey Steve, I’d argue that the fact a COM object may expose multiple interfaces allows for more flexibility at the cost of compile-time type safety but I generally agree with your observations.
  3. Well, all this talk about separating data from behavior is really a way to say “let’s return to the days of procedural programming, and, procedural programming without static type safety, at that.” That is exactly what SOA is all about: separate the data from the behavior and expect that to somehow solve everyone’s problems. Never mind that, if I have no way to associate the data with the correct behavior other than the conscienciousness of my programmers, chances are that, before long, data gets sent to the wrong behavior, and all hell breaks loose. This is not to say that dynamically-typed systems don’t have their place – they most definitely do. But to suggest that, from this, an “architecture” arises somehow is to sell snake oil. And, as I have preached many times in the past, if all we want is dynamically-typed systems, there is not need to invent all this WS and SOAP stuff. I can just use CORBA or some other middleware platform to pass XML strings around, and instantly, I have all the functionality of WS without any of the baggage. Michi.
  4. Aha! We absolutely agree Michi. Behaviour is moved away from the endpoint and is now captured by application protocols (e.g. workflows). That’s not to say that we are moving back to procedural programming (focus on calling remote procedures). It’s more like event-driven programming (focus on document exchange and notification about message arrival). It’s also the case, as you say, that you could use any implementation technology to do the same things. Yes, you could use CORBA to implement these ideas (many have been doing it for years) or even tcl/tk, perl, C and sockets. It’s not the implementations that matter but the design principles.
  5. I love your blog by the way, I am gonna have to add you to my list of watched blogs.

    http://www.websites-design.com.au/