/adr

Action-Domain-Responder: a web-specific refinement of Model-View-Controller.

Primary LanguagePHP

Action-Domain-Responder

Purpose

Organizes a single user interface interaction between a web client and a web application into three distinct roles.

ADR

Background

The term MVC has experienced some semantic diffusion from its original meaning, especially in a web context. (See this video from Stefan Priebsch for a more extensive discussion.) To resolve this diffusion, the Action-Domain-Responder pattern description is offered as a web-specific refinement of the MVC user interface pattern.

I think ADR more closely fits what we actually do in web development on a daily basis. For example, this pattern is partly revealed by how we generally do web routing and dispatch. We generally route and dispatch not to a controller class per se, but to a particular action method within a controller class.

It is also partly revealed by the fact that we commonly think of the template as the View, when in a web context it may be more accurate to say that the HTTP response is the View. As such, I think ADR may represent a better separation of concerns than MVC does in a web context.

Components

Action is the logic that connects the Domain and Responder. It uses the request input to interact with the Domain, and passes the Domain output to the Responder.

Domain is the logic to manipulate the domain, session, application, and environment data, modifying state and persistence as needed.

Responder is the logic to build an HTTP response or response description. It deals with body content, templates and views, headers and cookies, status codes, and so on.

Collaborations

  1. The web handler receives a client request and dispatches it to an Action.

  2. The Action interacts with the Domain.

  3. The Action feeds data to the Responder. (N.b.: This may include results from the Domain interaction, data from the client request, and so on.)

  4. The Responder builds a response using the data fed to it by the Action.

  5. The web handler sends the response back to the client.

Comparison to MVC (Model-View-Controller)

The dominant pattern describing web interactions is Model-View-Controller. Is Action-Domain-Responder really just Model-View-Controller in drag? We can see that the ADR terms map very neatly to MVC terms:

Model      <--> Domain
View       <--> Responder
Controller <--> Action

The two seem very similar. How are they different?

Overall, we can see from Fowler in his GUI Architectures essay that "there's not just one view and controller, you have a view-controller pair for each element of the screen, each of the controls and the screen as a whole." This is the primary element of semantic diffusion when applying MVC to web applications.

Here are some more comparisons of the individual elements in MVC vs ADR.

Model vs Domain

I can think of no significant differences here, other than that the Responder does not interact with the Domain in meaningful ways. The Responder might use Domain objects like entities and collections, but only for presentation purposes; it does not modify the Domain or feed information back to the Domain as described under MVC.

Controller vs Action

In common usage, most Controller classes in an MVC system contain several methods corresponding to different actions. Because these differing action methods reside in the same Controller, the Controller ends up needing additional wrapper logic to deal with each method properly, such as pre- and post-action hooks. A notable exception here is in micro-frameworks, where each Controller is an individual closure or invokable object, mapping more closely to a single Action (cf. Slim).

In an ADR system, a single Action is the main purpose of a class or closure. Each Action would be represented by a individual class or closure.

The Action interacts with the Domain in the same way a Controller interacts with a Model, but does not interact with a View or template system. It sets data on the Responder and hands over control to it.

View vs Responder

In an MVC system, a Controller method will usually generate body content via a View (e.g. a Template View or a Two Step View). The Controller then injects the generated body content into the response. The Controller action method will manipulate the response directly to set any needed headers.

Some Controller action methods may present alternative content-types for the same domain data. Because these alternatives may not be consistent over all the different methods, this leads to the presentation logic being somewhat different in each method, each with its own preconditions.

In an ADR system, each Action has a separate corresponding Responder. When the Action is done with the Domain, it delivers any needed Domain data to the Responder and then hands off to the Responder completely. The Responder is entirely in charge of setting headers, picking content types, rendering templates, and so on.

Note that a Responder may incorporate a Template View, Two Step View, Transform View, or any other kind of View system. Note also that a generic Responder may be used by more than one Action. The point is that the Action leaves all header and content work to the Responder, not that there must be a different Responder for each different View.

Comparisons to Other Patterns

These are some of the other patterns that are generally seen as refinements of, replacements for, or complements to MVC. See also the pattern discussion from Derek Greer at LosTechies.

EBI (Entity-Boundary-Interactor)

EBI appears to go by several synonyms: ports and adapters, hexagonal architecture, and ECB (Entity-Control-Boundary). It is further described as part of a Clean Architecture by Robert Martin.

EBI is in part an alternative to MVC where the core application elements and behaviors, represented by Interactor and Entity objects, are separated from the incoming and outgoing data streams by a Boundary. This has the effect of cleanly separating the application itself from the details of the input and output mechanisms, so that the core behaviors are never dependent on any particular element of the receiving and delivery systems. There is a great deal more to EBI architectures, such as "use cases" and "roles".

I confess to being unfamiliar with EBI, and so that description may be incorrect in whole or in part. It occurs to me from my limited reading that EBI may better describe domain interactions rather than MVC architectural patterns. If the above description is accurate, it appears that ADR maps only roughly to EBI:

  • the ADR Action and Responder elements may represent a web-specific EBI Boundary

  • the ADR Domain element may represent an EBI Interactor element, encapsulating or otherwise hiding the EBI Entity elements from the ADR Action.

Alternatively, in ports-and-adapters or hexagonal architecture terms, it may be reasonable to think of the Action as a "port" through which an EBI Boundary is invoked as part of the ADR Domain. Finally, the Responder could be seen as an "adapter" back through which the application data is returned.

Regardless, it does not appear that ADR is a direct replacement for EBI. It seems more likely that they are complements to each other.

DCI (Data-Context-Interaction)

DCI is described as a complement to MVC, not a replacement for MVC. I think it is fair to call it a complement to ADR as well.

MVP (Model-View-Presenter)

MVP has been retired in favor of Supervising Controller and Passive View. At first this seems like a candidate match for ADR, especially in that the Passive View and the Model have no dependencies on each other as noted on the Passive View page. From Fowler's narrative:

Supervising Controller uses a controller both to handle input response but also to manipulate the view to handle more complex view logic ...

A Passive View handles this by reducing the behavior of the UI components to the absolute minimum by using a controller that not just handles responses to user events, but also does all the updating of the view. This allows testing to be focused on the controller with little risk of problems in the view.

Let us examine a little more closely:

  • Model and the Domain map closely, as they do in MVC.

  • Passive View does not map well to either Action or Responder; it might better be regarded as the response that gets returned to the client.

  • Supervising Controller might map to Responder, in that it "manipulate[s] the view to handle more complex view logic". However, Responder is not responsible for interacting with the Domain, and it does not receive the client input, so does not seem to be a good fit for Supervising Controller.

  • Alternatively, Supervising Controller might map to Action, but the Action is not responsible for manipulating the view (i.e. the response).

In all, this seems a case of close-but-not-quite.

MVVM (Model-View-ViewModel)

MVVM seems to map only incompletely to ADR. The Model in MVVM maps closely to the Model in MVC and the Domain in ADR. Similarly, the View in MVVM maps closely to the View in MVC and the Responder in ADR.

However, the ViewModel does not map well to a Controller in MVC or an Action in ADR. Because ADR is a refinement of MVC, it seems reasonable to think that comparisons between MVVM and MVC would apply equally well to ADR.

For an extended description of those differences, please see these articles from Joel Wenzel, Avtar Singh Sohi, Rachel Appel, and Niraj Bhatt.

(In email discussions with an interested party, I was informed that MVVM is just like MVC, but with an added ViewModel to intermediate between the View and Model. If this is true, then a ViewModel is just as useful in ADR as it would be in MVC.)

PAC (Presentation-Abstraction-Control)

From Wikipedia:

PAC is used as a hierarchical structure of agents, each consisting of a triad of presentation, abstraction and control parts. The agents (or triads) communicate with each other only through the control part of each triad. It also differs from MVC in that within each triad, it completely insulates the presentation (view in MVC) and the abstraction (model in MVC). This provides the option to separately multithread the model and view which can give the user experience of very short program start times, as the user interface (presentation) can be shown before the abstraction has fully initialized.

This does not seem to fit the description of ADR very well.

RMR (Resource-Method-Representation)

I had not heard of RMR before it was pointed out to me by ircmaxell on Reddit.

ADR and RMR seem very similar, and seem to map well to each other:

Resource       <--> Domain
Method         <--> Action
Representation <--> Responder

However, some nuances of RMR make me think they are still somewhat different from each other. For example:

So in an OO language, a HTTP resource can be thought of as an object with private member variables and a number of public methods that correspond to the standard HTTP methods. From an MVC point of view, a resource can be thought of as a model with a bit of controller thrown in.

To me, this seems like mixing concerns just a bit too much. I'd rather see a cleaner separation of the domain model from the action being applied to the domain.

So the representation is like a view in MVC, we give it a resource object and tell it to serialize the data into it's output format.

There seems to be no allowance for other kinds of HTTP responses, such as "Not Found". That kind of response is clearly not a representation of the requested resource.

Having said all that, it may be that ADR could be considered an expanded or superset variation of RMR, one where a Resource and an action one can perform on it are cleanly separated into a Domain and an Action, and where the Representation (i.e., the building of the response) is handled by a Responder.

Models-Operations-Views-Events (MOVE)

From the originating site:

  • Models encapsulate everything that your application knows.
  • Operations encapsulate everything that your application does.
  • Views mediate between your application and the user.
  • Events are used to join all these components together safely.

This is an interesting pattern in itelf. The idea of Models and Operations seems to map well to Domain-Driven Design idioms.

However, I do not think MOVE is a close fit for ADR, specifically because of this paragraph:

Listening on events is what gives MOVE (and MVC) the inversion of control that you need to allow models to update views without the models being directly aware of which views they are updating.

In ADR, the Domain and the Responder do not "update each other". The Domain work is completed and passed to the Responder for it to present to the client.

Separated Presentation

There are hints of ADR, especially the Responder element, in Separated Presentation. Although the article is well worth reading, Separated Presentation sounds more like a meta-pattern that describes the general concern of separating data from presentation, not a specific approach to doing so.

Examples of MVC vs ADR

MVC Starting Point

An MVC directory structure for a naive blogging system might look like the following. Note that index and read present an alternative JSON type, and the comments template is a "partial" that also presents an alternative JSON type.

controllers/
    BlogController.php # index(), create(), read(), update(), delete()
models/
    BlogModel.php
views/
    blog/
        index.html.php
        index.json.php
        create.html.php
        read.html.php
        read.json.php
        update.html.php
        delete.html.php
        _comments.html.php
        _comments.json.php

Here's another type of MVC directory structure:

Blog/
    BlogController.php  # index(), create(), read(), update(), delete()
    BlogModel.php
    views/
        index.html.php
        index.json.php
        create.html.php
        read.html.php
        read.json.php
        update.html.php
        delete.html.php
        _comments.html.php
        _comments.json.php

A typical Controller class in MVC might look something like the following. Note that there are multiple actions within the Controller class, and that the action method deals with the response headers.

<?php
use Framework\Controller;

class BlogController extends Controller
{
    public function create()
    {
        // is this a POST request?
        if ($this->request->isPost()) {

            // retain incoming data
            $data = $this->request->getPost('blog');

            // create a blog post instance
            $blog = $this->blog_model->newInstance($data);

            // is the new instance valid?
            if ($blog->isValid()) {
                // yes, save and redirect to editing
                $blog->save();
                $this->response->redirect('/blog/edit/{$blog->id}');
                return;
            } else {
                // no, show the "create" form with the blog instance
                $this->response->setContent($this->view->render(
                    'create.html.php',
                    array('blog' => $blog),
                ));
                return;
            }
        } else {
            // not a POST request, show the "create" form with defaults
            $this->response->setContent($this->view->render(
                'create.html.php',
                array('blog' => $this->blog_model->getDefault())
            ));
        }
    }

    public function index()
    {
        // ...
    }

    public function read($id)
    {
        // ...
    }

    public function update($id)
    {
        // ...
    }

    public function delete($id)
    {
        // ...
    }
}
?>

The create() logic could be reduced somewhat by moving even more of the model interactions into a Service Layer, but the point remains that the Controller typically sets the response headers and content.

ADR Revision

In comparison, an ADR directory structure might instead look like this. Note how each Action has a corresponding Responder.

Blog/
    Action/
        BlogIndexAction.php
        BlogCreateAction.php
        BlogReadAction.php
        BlogUpdateAction.php
        BlogDeleteAction.php
    Domain/
        # Model, Gateway, Mapper, Entity, Collection, Service, etc.
    Responder/
        BlogIndexResponder.php
        BlogCreateResponder.php
        BlogReadResponder.php
        BlogUpdateResponder.php
        BlogDeleteResponder.php
        html/
            index.html.php
            create.html.php
            read.html.php
            update.html.php
            delete.html.php
            _comments.html.php
        json/
            index.json.php
            read.json.php
            _comments.json.php

The Action and Responder class pair corresponding to the above Controller create() example might look like this:

<?php
use Framework\Action;

class BlogCreateAction extends Action
{
    public function __invoke()
    {
        // is this a POST request?
        if ($this->request->isPost()) {

            // yes, retain incoming data
            $data = $this->request->getPost('blog');

            // create a blog post instance
            $blog = $this->blog_model->newInstance($data);

            // is the new instance valid?
            if ($blog->isValid()) {
                $blog->save();
            }

        } else {
            // not a POST request, use default values
            $blog = $this->blog_model->getDefault();
        }

        // set data into the response
        $this->responder->setData(array('blog' => $blog));
        $this->responder->__invoke();
    }
}
?>
<?php
use Framework\Responder;

class BlogCreateResponder extends Responder
{
    // $this->response is the actual response object, or a response descriptor
    // $this->view is a view or template system
    public function __invoke()
    {
        // is there an ID on the blog instance?
        if ($this->data->blog->id) {
            // yes, which means it was saved already.
            // redirect to editing.
            $this->response->setRedirect('/blog/edit/{$blog->id}');
        } else {
            // no, which means it has not been saved yet.
            // show the creation form with the current response data.
            $this->response->setContent($this->view->render(
                'create.html.php',
                $this->data
            ));
        }
    }
}
?>

Again, we can see numerous refactoring opportunities here, especially in the domain model work. The point is that the Action does not perform any Responder work at all. That work is handled entirely by the Responder logic.

You can review an extended set of sample ADR code here.

Commentary

Request Omission

A common critique so far has been that there is no "HTTP request" element present in the pattern. An earlier version of this document included a request under the title "Request-Action-Domain-Response". However, on further research into MVC and other related architectural patterns, I noticed that none of them define an input element. To stay in line with precedent, this pattern omits the incoming HTTP request.

Front Controller Omission

This pattern concentrates on the refinement of Model-View-Controller, and not on the entirety of web applications. Therefore, it intentionally omits some elements commonly found in web applications, particularly anything related to a Front Controller.

The ADR pattern does not describe a routing or dispatching element, nor how the Action and Responder relate to a dispatcher. Routing and dispatching are more properly the purview of Front Controller, and there are many ways for the Action, Responder, and any Front Controller mechanism to interact:

  • the Action may invoke the Responder directly, which then returns a response;

  • the Responder and response may be shared with a Front Controller so that it can invoke them directly;

  • the Action may return a Responder, which is then invoked to return a response, which is then invoked to send itself;

  • and so on.

The ADR pattern does not describe any pre-filter or request-validation elements, especially those that may be part of a Front Controller. Note that pre-filter or request-validation logic may or may not bypass the Action to invoke the Responder directly, or it may deliver a response of its own, or it may invoke a separate Action as a result of its logic, and so on. Likewise, the invoked Action may have its own set of pre-condition checks that cause it to invoke the Responder without ever interacting with the Domain. Reasons for these short-circuiting behaviors may include:

  • HTTP method negotiation. If the routing system does not map the requested HTTP method to the requested Action, the Front Controller may return an error response instead of dispatching to the requested Action.

  • Authentication. The presence or absence of client credentials, and their validity, may curtail the need to dispatch to an Action in the first place, or to interact with the Domain while in an Action.

  • Authorization. Access-control systems may deny the client's request for the given Action, or cause the Action to bypass interactions with Domain, and possibly return a response of their own.

  • Content negotiation. The Front Controller, Action, or other intermediary layers may negotiate the various Accept headers in the client request. Unsuccessful negotiation may pre-empt Action or Domain behaviors, and/or result in an early-exit response.

  • Content validation. If the incoming request data is malformed in some way, the Action might not interact with the Domain at all and move directly to interacting with a Responder to send an error response.

Alternative Formulations

This pattern may be better formulated as variations on Controller and View from Model-View-Controller instead of a pattern of its own.

That is, it may be that Action is a variation similar to Page Controller, and thus better termed an Action Controller. It would thereby fit into the Controller portion of MVC. (Indeed, the formal description for Page Controller says that it represents a "page or action.")

Likewise, it may be that Responder is a variation similar to Template View or Transform View, and thus better termed a Response View. It would thereby fit into the View portion of MVC.

Having said that, I believe those alternative formulations are probably not as good of a description of web-based interactions as is ADR. This is mostly because of the implicit interactions between Model and View in MVC. In MVC, the View updates the Model. In ADR, the Responder does not update the Domain.

Ambiguous Domain

Domain covers a lot: not just the business domain, but environment and application state as well. It might be better to call this a Model, but that too is somewhat ambiguous.

Additionally, it may be that the Action should pass a Presentation Model to the Responder instead of Domain data. But then, maybe the Domain service layer used by the Action returns a Presentation Model that encapsulates application state.

Regardless, recall that ADR is presented as a refinement to MVC. Thus, ADR has only as much to say about the Domain as MVC has to say about the Model.

Expanding Actions

One commenter noted that the Action element might be interpreted to allow for different logic based on the incoming request. For example, he noted that readers might expand a single Action to cover different HTTP methods, and put the logic for the different HTTP methods into the same Action.

While I believe the pattern implies that each Action should do only one thing, that implication rising from the Controller vs Action and RMR vs ADR comparisons, I will state it more explicitly here: the idea is that each Action should express one, and only one, action in response to the incoming request.

A Replacement For, Not Refinement Of, MVC

Nate Abele opines that the ADR pattern should be a replacement for MVC, one that applies to server-side applications:

I will say that the more I learn about MVC, the more I think it has little to nothing to do with server-side web applications. ... I think the biggest upshot of your ADR idea is that it provides us a clean break from what I have come to understand is a bad abstraction. My advice would be to avoid defining ADR in terms of MVC except where absolutely necessary.

Via http://paul-m-jones.com/archives/5993#comment-42425.

Other Commentary

The original blog post that led to this offering is at http://paul-m-jones.com/archives/5970.

Stephan Hochdörfer responded to that offering at http://blog.bitexpert.de/blog/controller-classes-vs.-action-classes; follow-up discussion appears at http://paul-m-jones.com/archives/5987 and http://www.reddit.com/r/PHP/comments/25y89a/stephan_hochdörfer_and_actiondomainresponder.

Jon Leighton writes about a "Focused Controller" that maps well to the Action element in ADR at http://www.jonathanleighton.com/articles/2012/explaining-focused-controller.

A follow-up post regarding View vs Responder is at http://paul-m-jones.com/archives/5993 with Reddit commentary at http://www.reddit.com/r/PHP/comments/26j3nf/the_template_is_not_the_view/ and http://www.reddit.com/r/webdev/comments/26j5o9/the_template_is_not_the_view_xpost_from_rphp/.

Akihito Koritama offers these notes: https://koriym.github.io/blog/2014/06/08/action-domain-responder/

Benefits and Drawbacks

One benefit overall is that the pattern more closely describes the day-to-day work of web interactions. A request comes in and gets dispatched to an action; the action interacts with the domain, and then builds a response. The response work, including both headers and content, is cleanly separated from the action work.

One drawback is that we end up with more classes in the application. Not only does each Action go in its own class, each Responder also goes in its own class.

This drawback may not be so terrible in the longer term. Individual classes may lead to cleaner or shallower inheritance hierachies. It may also lead to better testability of the Action separate from the Responder. These will play themselves out differently in different systems. Others have noted that "many classes" may be more easily manageable via IDEs and editors than "fewer classes but more methods" since class lookups are frequently easier than method lookups.

Acknowledgements

My thanks to the many people who have helped refine this offering, whether through questions, comments, criticism, or commendation. In no particular order, these include:

  • Matthew Weier O'Phinney
  • Hari KT
  • Stephan Hochdörfer
  • Adam Culp
  • Dan Horrigan
  • Josh Lockhart
  • Beau Simensen