Separating the concerns of routing and rendering

Separating the concerns of routing and rendering

I have been thinking about a good way to separate the concerns of routing and rendering. I was wondering what others in the Unfiltered community thought about this approach, so any reactions are welcome if you have the time.

Here are some pieces of a plan. The renderer needs to look at the various Accept headers in the request.

Re: Separating the concerns of routing and rendering

Konstantin,

I like the idea of separating the routing and rendering concerns, and while your approach does achieve this, I wanted to explore a bit further. I've pondered this same problem on various occasions, and I don't like the idea that the routing code separates itself from rendering by means of a continuation. While it does work, I wanted to aim for even less coupling.

It seems to me that routing is all about matching a request to an appropriate piece of logic. In terms of Unfiltered, I'll call this a ResourceIntent. Rendering is simply converting the result of an application function into a format suitable for the client; I'll call this a ResponseIntent. A ResponseIntent needs the model produced by the ResourceIntent, as well as the request, so that it can determine how to render the model. Let's say these are wrapped in a type ModelPackage:

This is just like a normal plan, except the rendering logic is gone (only application logic remains). Elsewhere we define a ResponseIntent, whose argument will be type-compatible with a ResourceIntent's result:

The argument here is a ModelPackage; a Tuple2 of the request and some intent-specific model type. This allows rendering to be tailored based on the request. With a ResourceIntent `f` and a ResponseIntent `g`, we can compose a full Intent while keeping the routing + app logic separate from the rendering:

class App extends unfiltered.filter.Plan {
import Plan._

val intent = f andThen g
}

I've put together a rough prototype that uses this approach, so I expect the above code *should* compile, provided the missing pieces (userRepository) are filled in. I'd like the community's feedback on this, as well as the separation problem in general. Thanks for Unfiltered, and thank you Konstantin for starting this discussion.