Michi Henning had some comments on my “Loose-coupling through the relaxation of endpoint assumptions” entry but pblog had trouble accepting them due to an ASP.NET safeguard (not accepting XML elements in a textbox from HTTP POST). I will fix this and will also allow formatting for comments so that long comments like this will not appear as a single paragraph.
For now, and after getting Michi’s permission, I am posting his comments bellow. I will reply in another blog entry.
“URIs are used as addresses/names rather than as identifiers that uniquely and unambiguously identify a resource independently of any particular transport/transfer technology.”
Let’s look at one of those URIs: It has a protocol identifier “http” or “ft”, it has an endpoint identifier “example.org” or “mymachine.mydomain.com”, and it has an identity: “resource1″ or “pub/docs/doc1.txt”. Incidentally, this is exactly the same information that is present in a CORBA IOR, which also contains a protocol identifier, and endpoint identifier, and an identity. No difference here at all. And, when one thinks about that, it’s not surprising: to do any remote communication, I must have those three items of information: the protocol (“which language do I need to speak?”), the endpoint (“which machine do I need speak to?”), and the identity (“which particular thing at that machine do I need to speak to?”). Whether you call these things URIs or IORs does not make one iota of difference – either addressing mechanism contains the same three fundamental items of information; to infer architectural differences between Web Services and distributed object systems from the difference between URIs and IORs is to fall into the trap of confusing syntax with semantics.
You also say:
“In service-orientation, and in particular in MEST, we remove interfaces from the endpoints of our actors. Our services don’t support interfaces but, instead, they receive/send messages. [...] Contracts are put in place to describe/govern the exchange of information between services.”
I don’t see how this is any different from an object with an interface. With Web Services, an endpoint understands a set of message types that are agreed upon a-priori (or are described by something like WSDL). How is this different from giving an interface to an object, with each method corresponding to a message type? After all, sender and receiver must agree on what kinds of information they exchange and how to interpret that information. Whether you call that a “message” or an “interface” is irrelevant. Both mechanism do nothing more (and nothing less) than to describe a set of message types that can legitimately be used to communicate via a particular endpoint.
You go on to say that the Web and REST (just like CORBA) suffer from the problem of dead references. I agree. You also go on to say that some web server implementations do something to help mitigate the problem. I assume that you are referring to automatic forwarding of a request to a page’s new location, or doing something akin to directing the client to a search engine. But nothing of this is new, and CORBA (and other distributed object systems) have similar mechanism of redirecting clients.
You then say:
“Objects are associated with some piece of state that can be referenced across the network. That state is associated with the endpoint from which the object is made accessible.”
That’s not correct. The state is not associated with the endpoint, the state is associated with the object. The object can change endpoints freely, or be replicated at multiple endpoints and present the same state. The reality is that the state (if there is any) is associated with the object identity, not with the endpoint.
“In traditional object-oriented computing every object is assumed to have state. That’s part of the definition of an “object”. If one suggests that objects do not have state (at the architecture level), then I would say that they are not talking about objects but about something else. Anyway…”
I think that this is glossing over an important point. As you say, traditionally, we think of objects as state, plus operations that operate on that state. But, just as importantly, objects need not have state. Whether we are still talking about objects in that case is simply an academic distinction. The point is that I can build stateless objects with CORBA just as well as I can build stateless services with WS. And I can build stateful services just as well with WS as I can build stateful objects with CORBA. There is no fundamental architectural difference here between WS and distributed object systems. It is simply a design choice whether I keep state on the server (in the service or object), or whether I keep it in the client.
The bank example you show is a bit strained and, again, does not expose any fundamental differences between WS and distributed object systems. In particular, the identifier that identifies the ultimate account is completely secondary to the nature of the interaction. In your example, you say that you give the bank an identifier such as “creditcard:mastercard:777-888-999-000″ and then claim that this is “talking *about* the resource”, not *to* the resource”.
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. The nature of the interfaction does not change one iota either way.
You then go on to show the object-oriented version of the bank example.
Incidentally, a more object-oriented design would use
instead of passing the account as a parameter to the bank, which is what you show:
bank.PayMoney(account, card, 1000);
But, in a sense, this just underlines what I said: whether the identifier is passed as an explicit parameter or is buried inside an object reference is a third-order issue, at best. In either case, the identifier is sent over the wire to the receiver so, logically, there is no difference whatsoever in the information that is exchanged. Given that the same information travels over the wire in either case, how on earth could there possibly be any architectural difference?
Finally, you talk about “loose coupling”. Now, for the life of me, I cannot see why one way of doing things would be any looser coupled than the other. The following points hold for both WS and distributed objects:
1. The handles we use to identify addressable entities (URIs or IORs) contain a protocol specifier.
2. The handles we use to identify addressable entities (URIs or IORs) contain an endpoint identifier (such as a domain name).
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).
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.
5. Both approaches exchange the same information over the wire: the identity of the accounts involved, and the amount of money to be transferred.
6. Sending a message that is unexpected or is internally malformed causes an error with both approaches.
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?)
So, exactly where is this “loose coupling” then? What exactly is it that is “loosely coupled” here? What is it that I can change in either system without breaking things? What actions do I need to take when I want to change things? And what happens with respect to backward compatibility in either case?
The answer is that, whether I use WS or distributed objects, the actions I need to take when I change something are the same, as are the consequences of taking these actions. The two things, WS and distributed objects, are exactly identical, at least when it comes to architecture and semantics. Any differences are to be found at the implementation level, in things such as ease of use, speed, available tools, vendor support, etc.
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.