I would like to start this entry by just saying that comparing REST and Web Services technologies is like comparing apples and oranges. One is an architectural style, a set of principles, while the other is a set of technologies for distributed computing.
Comparing REST vs Service-Orientation may be interesting but I feel that it would be even more valuable to discuss resource- vs service-orientation. What follows is an attempt to do just that by considering aspects of the resource-, object-, and service-orientation paradigms for distributed computing. Hopefully, other bloggers will throw more views into the mix to continue the discussion. Of course, I will make sure that the ProcessMessage architectural style is mentioned somewhere in there :-)
Before I even start, let me just say (by paraphrasing) that I am a firm believer of using the right “architectural style” for the right solution. I am not promoting any particular paradigm or set of technologies as the panacea for distributed computing. However, I believe that when trying to take an informed decision, a good understanding of the issues involved must exist. This is an attempt on my behalf to better understand these issues.
The approach I propose here is based on identifying the building blocks of each paradigm…
(Disclaimer: the descriptions bellow of the paradigms and their building blocks are by no means normative)
In object-orientation we find objects and interfaces as the building blocks for distributed applications. Interfaces describe the behaviour of objects through a set of methods while each object has an identifier and some (encapsulated) state (a set of attributes). The communication abstraction is the method invocation on objects (local or remote). At the architectural level, we design applications by identifying the behaviours, then describing them (using something like an IDL), and finally define the sequence in which methods are invoked.
Here are two examples of two methods of an interface:
Appointment CreateAppointement(Person, DateTime);
There are many issues that I won’t even touch (e.g. state management, concurrency, pass-by-reference vs pass-by-value, etc.). I will concentrate only on the communication aspects based on the example above.
There is an implicit assumption that returning an Appointment object signifies the completion of the execution of the behaviour associated with the particular method (CreateAppointment). An implementation may, of course, continue doing other stuff after the Appointment object has been returned but the transmission of information back to the caller carries “method-completion” semantics as far as the caller is concerned.
Some may think that the second method doesn’t carry such semantics because it’s “void”. Well, this is not-true. Even “void” methods carry method-execution-completion semantics. (Thanks to Mark Little who confirmed this in a recent discussion I had with him). We’ll come back to this soon.
In the resource-oriented paradigm we don’t have the notion of behaviour/interfaces. Instead, we only have state (the resource) that is transferred around. It is through the transfer of state that program logic is captured and applications are built. REST is an architectural style that lives in this world. While resource-orientation is the paradigm, REST is one possible set of principles that govern it (there can be others… CRUD, ARRESTED).
REST suggests that a uniform interface with constrained semantics may be used to operate on resources identified through URIs. The scheme part of the URI identifies the application protocol to be used; the one that defines the available operations and their semantics. To some it may seem similar to object-orientation but it’s not. Here, the building blocks are the resources and the constrained interfaces. While objects are “callable” entities (we call “methods” on them) resources are not. Instead, we call operations on agents. An agent receives the instruction to perform the operation with the uniform semantics on our behalf. All operations related to a particular resource.
Some may note that a CORBA ORB is really doing something similar… it calls methods on CORBA objects on the caller’s behalf. I won’t argue against that but I will point out that now we are really talking about implementation and not about architecture. My discussion in this post focuses on the architectural aspects; how we approach the design of a distributed application in terms of the building blocks available to us, and not the implementation details. From an object-oriented architecture point-of-view, the existence of an ORB is irrelevant. I am ready to be corrected on this by the CORBA experts.
The example from above becomes (when HTTP is used as the application protocol):
I use the following notation to describe what’s going on (is there a standard way of describing this?): POSTstate TO uri ANDSEND response
InformationAboutNewAppointment TO http://calendar.com/appointments AND SEND created, http://calendar.com/appointments/123
Another way would be to use PUT on a predefined URI but that’s not important for this discussion.
If we want to access the information about the new resource, we have to do a GET:
which results to a response that contains the state representation of the appointment.
Hopefully the difference between object-orientation and resource-orientation is clear. Now for the final bit…
In service-orientation the building blocks are the services and the messages. In absence of a widely-accepted definition of a service, I’ll give you Jim‘s and mine:
A service is the logical manifestation of some physical or logical resources (like databases, programs, devices, humans, etc.) and/or some application logic that is exposed to the network;
Service interaction is facilitated by message exchanges.
Service-orientation is all about building distributed application using services and messages as the building blocks. Different people have suggested various principles that should govern the paradigm. I particular like the 4 tenets suggested by the Microsoft folks but that’s a personal preference.
Please note that there is no concept of an operation, method, or call. Our building blocks, the services, are not callable entities and it is not suggested that the messages represent calls. Yes, methods on objects in a traditional distributed object-oriented system can be implemented as message exchanges. However, when we architect object-oriented applications we don’t use the message abstraction; we the method invocation abstraction. In our example above, we wouldn’t say that a message is sent to invoke the CancelAppointment(Appointment) method and a message will be returned as a result indicating that there is no result but that the execution has been completed. That’s what may be happening at the implementation level but that’s not how we reason in our design of the system.
It is through the exchange of messages, the message exchange patterns that we model application logic. We put contracts in place to define the formats of the messages being exchanged and the exchange patterns a service may support. Our example in a service-oriented setting looks like this:
AppointmentRequestMsg TO CalendarService FROM sender
AppointmentInformationMsg TO sender FROM CalendarService
AppointmentCancellationMsg TO CalendarService FROM sender
Please note the one-way message above (unique in service-orientation). In object- and resource-oriented architectures we couldn’t reason in terms of one-way messages because our building blocks don’t support it. Yes, we could “simulate” one-way messages by ignoring voids or through asynchronous method invocations but that’s a convention at the implementation level rather than a characteristic of the architecture.
In the above example, the transfer of a message from the sender to the calendar service is what Jim and I call the “ProcessMessage” architectural style. ProcessMessage is to service-orientation what REST is to resource-orientation. If one wishes to see services as entities that support operations, then there should only be one operation called “ProcessMessage”. That operation is defined as:
A call to the “ProcessMessage” operation represents the transfer of a message from a sender to a recipient and a request to process that message.
The semantics of the “ProcessMessage” operation are uniform across all services. The ProcessMessage operation must not be represented as
I have been guilty in the past of presenting it as such but I will not do that again. The above representation may suggest to some that there is a response message (for “void”) which is not the case. From now on, ProcessMessage should be represented as:
msg -> ProcessMessage()
ProcessMessage(msg) (i.e. no void)
When a service-oriented system is realised, the “ProcessMessage” operation is replaced by an appropriate mechanism from the underlying transport technology (e.g. socket receive(), SMTP command, HTTP POST, etc.) but the semantics of message transfer remain the same. It is important to understand that unlike the REST verbs, the ProcessMessage operation is a logical one and the only purpose of its existence serves is to make it easier for those who would like to reason in terms of callable entities to understand that concepts of service-orientation.
Now, compare this approach to architecting a distributed system to that between the resource- and object-oriented paradigms. It is my thesis that due to the minimum assumptions and the luck of a set of verbs with particular semantics that can be called is what enables loose-coupling.
Of course, the CORBA people will say that they’ve been doing this for ever (like everything else). Indeed, above I’ve talked about architectural paradigms and not particular implementations. As I have said before, one may use CORBA or Java RMI to build a service-oriented solution in the same way C and FORTRAN could be used to implement an object-oriented application. You just choose the right tool for the right job, and it is my belief that the set of Web Services technologies could be the right tool for service-oriented applications. I am still to be persuaded by the REST camp that this is not the case.
A note on verbs and semantics
There has been a lot of discussion on the use of verbs and associated semantics in the world of WS. The introduction of WS-Transfer has fuelled the discussion. I was glad when I read (look for commentary on Chris Sells‘ XML Dev Con 5) that WS-Transfer is not considered as an important specification in the grant scheme of things. That’s good, because WS-Transfer promotes a resource-oriented view.
I actually disagree with the existence of the wsa:action header found in WS-Addressing. The action header promotes the view that the receipt of a message will result in the execution of a particular, sender-identified action. The action is also seen to carry the semantics of what it means to receive the message. Why do we need to make such assumptions?
One could ask: “if not wsa:action, what?” Well, I believe that we don’t need to carry semantics associated with an operation. The semantics of the message exchange are defined by the protocols in place and by the specifications of the documents being exchanged. The protocol(s) in place tells us what will happen, in terms of messages exchanged, when a particular document is processed. The specification of the document (defined in a particular namespace) may give us the required semantics (but not necessarily). Why do we need additional ones in the message?
In our example, all I need to send when I want to create a new appointment is a message of the form:
Why would I need an action in this case? I think that this message is self contained and carries all the information I require to implement my application.
I hope I have illustrated the main differences I see between the building blocks of the three paradigms I considered. I am keen to receive corrections/comments on all of the above.
I hope people now see why I have been negative about the use of WSDL as another object IDL and why I have suggested (I admit, not with a good explanation of my thinking) that exposing CORBA objects as Web Services will hurt the efforts to better understand and promote service-oriented, message-focused architectures. I understand the business need though.