API Design: Appreciating models and controllers

When your system’s API isn’t RESTful, standard ways of grouping actions based on data resources and routing are not guaranteed – if at all considered.

Note: I’ll use PHP scripts as an example, but the points aren’t limited to that language.

Worst case

An API endpoint for your system is a separate file (like getPosts.php) that does all of the work itself: database fetches, data transformations, and response generation.

Cons:
* no logic reuse across api endpoints/files (in fetching and transforming data),
* likely to vary attribute names in the response (especially if multiple developers are creating these apis). What was ‘postAuthor’ in one api is simply ‘author’ in another…
* directory bloat (i.e., a large number of files/endpoints).

Better case:

An API endpoint is a separate file that defers the data-oriented work to a function of a class (aka model), but still handles it’s own responses.

More concretely, the file getPosts.php could instantiate a Post class (model) – calling a hypothetical function of that class like getPosts(). The script getPosts.php would then json_encode the returned value of the function, set up some success-indicating errorCodes, and then echo a valid response.

Pros:
* reusability by way of function abstraction
* standard naming across files if you use the attributes of the Post class

Cons:
* directory bloat (a file per api endpoint)

An even better case

All related actions (like getPosts, createPost, deletePost, and updatePost) go through a single class/file, not 4 separate files. Each function of this class implements one of those actions. Hence, each function controls the manipulation of the model and generation of the response. Let’s call that class the PostController. See where I’m going with this?

When a request comes in (for one of the aforementioned actions), the PostController gets instantiated. Based on the requested action, we’ll call the appropriate function of the PostController instance. That function can then manipulate an instance of the Post class and generate the API response.

Pros:
* reusability by way of the model (similar to the previous solution), but also in terms of reducing boilerplate code for response generation (setting up error codes, preparing the response format, and possibly more)
* standard naming via the model (similar again)
* no directory bloat since the four actions (getPosts, createPost, deletePost, and updatePost) have been reduced from 4 separate files into 4 functions within 1 file.

Cons:
* We now have to build routing logic that determines which controller to instantiate (and subsequently which function of that controller to call) based on a requested action.

The routing logic seems like a small price to pay for the cleanup and clarity of the new, controller-driven method.

This actually isn’t a new concept – it’s more than 40 years old: the Model View Controller design pattern. Views: visual representations of the model data with controller-backed interactions, aren’t really applicable in a JSON-serving API.

If you’ve used Rails or another MVC-based framework, you’ve already come to accept this design as dogma; however, it’s great to see where and why it shines.

Comments

comments