I explained last month that the Web Style never took off since Tim Bray's post which predicted the End of SOA with a great dose of exaggeration. Instead, developers quickly pivoted towards a traditional, albeit HTTP-based, API style. I am not sure who could have expected a different outcome...
Over all these years, the process of defining an API style has been quite chaotic as people swung from resource orientation to service orientation and back and, today, there is no particular "standard" or "sytle" which people can confidently rely on to design their API. Every developer decides artistically (and often inconsistenly) how he wants to use HTTP to marshal a particular API call.
I was doing some research on a few APIs this week-end and took another look at Swagger. I really like what they do. Incidentally, if anybody doubts that the Web Style is dead, I would take a peak at their petshop demo... That being said, that petshop API is terrible.
When it comes to Resource Orientation, Service Orientation, Event Orientation... I actually like them all: I am a polyadic programmer (a.k.a metaprogrammer) and if anyone doubts it, I would encourage you to look at a framework I built in 2007, WSPER ("whisper"), which stands for Web Service, Process, Event, Resource.
So, after so many years, should we continue opposing Resource and Service orientation? I would like to show here that indeed we can weave them together and as a result we can surface resource orientation in a much cleaner way while retaining all the benefits of service orientation in the process.
Services have this nice property of being exposed via an endpoint which creates a natural boundary of deployment, access and management. Enpoints truly enable an interface to be totally decoupled from its implementation. I cannot emphasize enough how important that decoupling is to distributed computing.
Service orientation brought another important innovation by moving the focus from objects to views. Services hide, just enough, the model behind the Service Interface: you understand that you are dealing with a customer view, but you never have to understand the extend of a customer class. Actually I would argue that a "true" customer class cannot be built, you always interact with a particular resource in a given context (Sales, Billing, Support...). If you doubt it, please take a look at this presentation from Daniel Jacobson. To be fair, however, as a side effect of this approach, queries or (partial) updates are notoriously painful when you need to define them in a WSDL, no argument there. I get that. But is that a reason to give up altogether on Service Orientation?
On the other hand the RESTafarians have grossly misunderstood Service Orientation and at the same time, never really understood that REST brought back distributed objects into the programming model. Worse, because of its sole focus on resources, REST forces you to expose every single detail of the model to the resource consumer. It shouldn't be a surprise to anyone that in the end all they can deliver is CRUD.
One thing that people often forget in SOA is that a service implementation can and should expose multiple service contracts, this is a bit harder to do with APIs because resource paths are not polymorphic by design and they are often hard wired to the implementation. Yes, I know, I could use yet another HTTP header or even concatenate an nth parameter to the Content-type but that is inelegant because you lose the natural boundary offered by a "service" endpoint. The real elegance I see behind Resource orientation (that none of the Web APIs I know implement, including the so-called RESTful ones) is when a path, say /customers/1234, truly points to the same resource regardless of which service I use to access it.
What I mean here is when you have a customer service and a purchase order service, you should be able to fetch the same customer information (albeit with a different view) using the same path. Yes, when you query an order GET /orders/345 and it returns a path (not a link) to /customers/1234 the PO Service could implement that path and return a view of the customer in the context of a purchase order and if you invoke the same path on the customer service, you would get a more complete view (assuming you have the access rights).
When you marshal the service name within the path (and the version for that matter), you cannot achieve that elegant behavior without a lot of out of band conventions and string manipulations. That's why I suggest that you define the service/API boundaries using subdomains rather than paths. For instance we would have two subdomains:
Each subdomain would provide access to its own resource collections, including overlapping collections. Both API calls would return a view of the same customer:
Imagine a "link" that works across services and versions? Many times I have mentioned that REST couples access and identity. Here we have a clear separation between access (subdomain endpoint) and identity (at the path level).
This API style is capable of supporting the traditional Query/Create/Update/Delete operations quite efficiently, in a way that could look attractive to Web developers. However, I would like to emphasize again that HTTP alone is not a good query language. If you doubt it,please take look at the MongoDB API. Any serious service implementation will have to follow their lead, especially for complex queries and partial updates.
Actions are another point of misalignment between resource and service orientation. Many of the RESTafarians have dismissed the need for actions and advised to use a simple update instead. In the real world this is of course a completely bogus approach (validation, authorization, logging, throttling, versioning, duplication of the update logic on the consumer side...) and Web API designers have been prompt to use action verbs in their paths, wired to specific method calls.
I have also explained many times that every resource has a lifecycle. Some resource (such as a Tweet or Status) have a CUD lifecycle (Create -> Update* -> Delete) which maps well to the lifecycle of a Web page, and hence the HTTP verbs, but again in the real world, lifecycles are complex and each transition maps to an action which you need to express one way or another. A verb is what everyone understands. If you want to use a noun for a verb, be my guest, but semantically you have not changed the meaning of the call, you are still triggering an action, which when successful will transition the resource from one state to the next.
So there is no reason to be afraid (and do like everyone else): use verbs as needed, either behind a resource when they apply to a resource instance or at the collection level when you initiate a lifecycle.
Composite calls (which fire several actions that transition to multiple states on different resources) should also be considered.
You will notice that the use of a subdomain as a service boundary greatly simplifies the migration from one major version to the next since API signatures and identities are not polluted by the version number like it is often the case in traditional API designs.
That's it, I believe that approach defines a clean Web API style which brings together the best of Resource Orientation and Service Orientation:
This post has 1 feedback awaiting moderation...