Sound advice - blog

Tales from the homeworld

My current feeds

Mon, 2008-Oct-06

REST Model-View-Controller (cont.)

My last article introduced the application of model-view-controller to REST client and server design. I thought I would expand on that material a little further, and provide some diagrams as food for thought.

The REST Triangle

The first thing I would like to do is re-introduce the REST triangle, roughly in line with the terminology I have been preferring of late.

REST Triangle

When I first introduced the triangle we were all talking about nouns and verbs, and the obviousness of preferring to have more nouns than verbs. Honestly, I think it caused as much confusion as it did clarity. The obviousness is hard to draw from a set of natural languages that have a lot of both nouns and verbs, and the analogy doesn't really help people come to grips with the impact on their architecture.

Instead I have been tending to talk about Resources and Patterns, URLs and Protocols. Resources are network-exposed objects, and tend not to be that easy to grasp until you relate them directly to the concept of URLs. People get URLs. They understand what they look like and roughly what they are for. If you can stick to that end of the story you shouldn't get too much blow-back.

I prefer Patterns to "verbs". I think the verb or method concept is too low level, and doesn't really give an architectural feel for why limiting them is useful. Patterns on the other hand give in my view about the right feel for the real architectural motivation. People understand that you want to catalogue and reuse patterns. Every message exchange is part of some sort of pattern when you operate over an unreliable network. There are always message retries or redirection, or an imperative not to retry, etc. Communication at this level is hard for the uninitiated, and keeping to a fixed set of well-understood set of patterns only makes sense.

Part of the Uniform Interface constraint is that patterns are baked into protocols. You don't talk about a GET pattern, then make up a new method every time you apply it. You make the method name match the pattern. This bypasses much of the service discovery jiggerypokery that you would otherwise have to do. If you already know what pattern you want to apply, and the server supports that pattern, then you know which method to call. After that it is just a matter of making sure you have the right URL in hand to complete the request.

It almost goes without saying, but I'll say it anyway: Any pattern in REST architecture must of course conform to REST principles. That does not mean that the set of patterns is fixed by any means, but we have gotten away with surprisingly few patterns so far.

Squeezed away in the corner there you might call Content Types, but that doesn't really mean much to many people. I tend to call them document types these days. That has a reasonable mapping for most people to what we really mean when we talk about document types: An Excel document isn't much use to Word. A Word document isn't much used to Powerpoint. They are different document types.

The separation of Document Types and the patterns that transfer documents around the network is evolvability gold. You can introduce new kinds of information into the architecture as new document types without breaking your existing infrastructure for moving documents around. However, we also want to catalogue and reuse Document Types. We want to reuse our parsers and generators. We want to reuse our human understanding of what a particular document actually means. Your patterns will likely even let you negotiate between client and server to ensure that everyone can relate the conversation to a document type they were designed to understand.

Coding the Triangle

By no means is there only one way to implement a service or a client. One of the big selling points of REST and SOA generally is that they can operate in a heterogeneous environment where different technology and approaches are used on either side of the client/server divide. With that in mind, let's look at what I would consider a basic "body plan" for a REST client and server. I'll focus on an application of the GET pattern for purposes of example, but the same plan fits any pattern you would care to mention.

REST MVC Body Plan

Model-view-controller as a pattern means many things to many people in many different contexts. Its exact meaning has been somewhat eroded over time and replaced with specific variants for special situations. The overall goal of the pattern, however, has not changed: Isolate appearance from implementation. Separate application logic from the constraints of how that logic is presented to users and to other applications.

Down the middle of my plan I have placed the corners of my REST Triangle. I have the set of Document Types in the architecture. I have the set of Patterns and Protocols. I have the set of Resources or URLs in the architecture. These are abstract entities included to give the diagram a bit of context. Each resource participates in a defined subset of the architecture's overall set of Patterns and protocols, and provides the public face to application logic behind the scenes between client and server.

Down the bottom right and left corners of the diagram I have the client and server model components. These components rightly do not depend on anything and do not play any public role in the message exchange. They are shielded from view by their respective Controllers.

The client controller includes code to pilot a pattern's state machine from its initial to its terminal state, despite possible adverse network conditions along the way. Lost response? Retry the request. Redirection? OK, follow if it looks reasonable. Fail the pattern if it looks dodgy. Positive response from the server? You beauty. Process the response.

The role of the server controller is quite different. It will include objects in its scaffolding to handle requests to any of its Resources. That might mean one object per resource. More likely it will mean an object tree with parameters along the path that collect a number of different resources together. These parameters will be passed into the appropriate function on a scaffolding object.

Let's say our request was a GET to http://example.com/light-bulb/1234. Chances are the request would arrive at a function internally identified as something like Resources.light_bulb.get(id). The controller component will then consult its model as part of fulfilling the request:

Resources.light_bulb.get(accept, id):
	bulb = Model.get_bulb(id);
	return Transform.to_acceptable(accept, bulb.status);

As well as consulting the model, this server consults its set of transforms. It wants to return information about the light bulb in a form that the client understands. Therefore, it uses the accept header from its request and passes it through to the Server Transforms component. Multiple controller functions may return the same Document Type and make use of the same transformations to prepare the returned document.

Once returned, the client will also apply its own transform to the document. Its objective is to map the document into data structures that be easily applied to its own model.

Conclusion

Client and Server Controllers deal with the set of Resources and the application of Patterns and Protocols. The Models of each will be queried and updated as part of the transaction, and the Server Model contributes to the set of Resources by containing objects that can be looked up through the Server Controller. Transformations at both end relate documents to model-ready data, and model data to documents.