Action-Domain-Responder - finalizing MVC for web tasks

Lecture



purpose


Divide the user interface interactions between the web client and the web application into three well-defined roles.

Action-Domain-Responder - finalizing MVC for web tasks

Background


The term MVC experiences some semantic blurring of its original meaning, especially in the context of the web (see the video by Stefan Pribsh for a more detailed discussion of the issue). As a means of eliminating this blur, I bring to your attention a description of the Action-Domain-Responder pattern , which is a refinement of the MVC concept for the needs of solving web-specific tasks.

I believe that ADR is much better in line with what we actually implement in the day-to-day web development process. For example, the creation of this pattern was partially inspired by the way we solve the problems of routing and scheduling, because in the general case we do not apply to routing and schedulingto the controller class per se, and to some specific action method in this controller class.

Another problem that has come up is the fact that we often view the View as a template, although in the context of the web it would probably be more appropriate to say that the View is an HTTP response. Based on the foregoing, I believe that ADR is able to provide a better separation of concepts for web applications than MVC.

Components


An Action is the logic that connects a Domain and a Responder . It uses input data to interact with the Domain and passes the output of the Domain to the Respondent .

The Domain contains the logic for managing the session, application and environment data, changing the state and managing the data if necessary.

A Responder is the logic needed to create an HTTP response or description of a response. It operates with basic content (body content), templates and views, headers, cookies, status codes and more.

Interactions

  1. The web handler receives the request from the client and passes it to the Action .
  2. The action interacts with the Domain .
  3. The action transfers data to the Respondent (this data may include the results of interaction with the Domain , data from a client request, etc.)
  4. The responder generates a response using the data received from the Action .
  5. The web handler sends a response to the client.

Comparison with MVC (Model-View-Controller)


The most popular pattern that describes interactions within the web is Model-View-Controller . Is the Action-Domain-Responder really just a disguised Model-View-Controller ? You may notice that the ADR elements are quite clearly reflected in the MVC elements:

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


The two patterns look very similar. What are the differences?

In general, from Fowler’s essay “ GUI Architecture ” [translation here ], we can see that “you have more than one view and one controller, you are forced to create a separate view-controller pair for each block on the page, each control and for the page as a whole. " This is the main element of semantic blur that occurs when applying MVC in web applications.

And let's compare in more detail the individual components of MVC and ADR.

Model and Domain


I do not see radical differences in them, except that in ADR, Responder does not interact with the Domain in any significant way. The responder may use Domain objects as entities and collections, but only for display purposes; The respondent does not modify the Domain and does not send him any information, as provided for in the framework of MVC.

Controller and Action


In the general case, most classes of the Controller in a system designed according to the principles of MVC contain several methods, each of which corresponds to a particular action. Since all these methods live in one Controller , an additional “wrapping” logic is also added to it to work with each of these methods, for example, hooks that fire immediately before or after the action itself. A significant exception to this rule is microframes, in which each Controller is a separate closure or called object, which is more consistent with the Action (e.g. Slim ).

As part of the same ADR Actionsseen as separate classes or closures. In other words, each Action must be placed in its own separate class or closure.

The action interacts with the Domain according to the same principles by which the Controller interacts with the Model , but does not interact with the View or template system. The action simply transmits the data to the Respondent and allows the latter to independently manage it.

Representation and Respondent


In the MVC system, the Controller method typically generates the response body using the View (for example, via the Template View or Two Step View ). The Controller then integrates the generated response body into the response itself. The Controller method , which is the action, directly controls the response to set the necessary headers.

Sometimes part of Controller methodscan provide different response formats for the same data. Since this variability is most often not supported by absolutely all methods, the logic of data presentation somehow changes from method to method, with its own conditions in each case.

In ADR, each Action has a corresponding Responder . When the Action finishes its interaction with the Domain , it transfers from the Domain to the Respondent both all the necessary data and control over this data. The responder fully controls the setting of headers, the choice of content types, the rendering of templates and more.

Note that the Respondentmay include Template Engine , Two-Step Template Engine , Transform View, or any other presentation system. Also note that a generic Responder can be used by more than one Action . The main thing is that the Action leaves all the work on the headers and content to the Respondent , and not what is required to create its own Responder for each individual Submission .

Comparison with other patterns


There are other patterns that are considered as refining, replacing, or complementing the MVC concept. You can check out this review of patterns by Derek Greer .

EBI (Entity-Boundary-Interactor)


There are several synonyms for the EBI term : ports and adapters, hexagonal architecture, ECB (Entity-Control-Boundary). It is described as part of Robert Martin's Clean Architecture .

EBI is a partial alternative to MVC, in which the basic elements and behaviors represented by Interactor and Entity objects are separated from incoming and outgoing data using Boundary . The main consequence of this approach is a clear distinction between the application itself and the intricacies of input and output mechanisms, so that key behaviors never depend on any particular system for receiving a request or sending a response.

I admit, I am not very familiar with the concept of EBI, so this description may not be entirely correct in general or in particular. After my incomplete research in this area, I came to the conclusion that the EBI architecture probably describes the interactions within the application better than the MVC. If the above description is correct, then ADR fits pretty well on the EBI structure:

  • ADR's Action and Responder can serve as a web-specific Limiter
  • The ADR Domain can serve as an analog of the Interactor , encapsulating or otherwise hiding elements of EBI Entities from the ADR Action


Alternatively, ports within the terminology and architecture adapters or hexagonal, may be more reasonable consideration of actions as "port" through which EBI'yny limiter is invoked (invoke) as part ADR'nogo Domain . Finally, Responder can be thought of as an “adapter” through which the application returns data to the client.

However, ADR does not seem to be a direct replacement for EBI. Rather, the two approaches complement each other.

DCI (Data-Context-Interaction)


DCI is described as an addition to MVC , not a replacement. I think it would be fair to call it an ADR supplement to the same extent.

MVP (Model-View-Presenter)


MVP has been retired by the Supervising Controller and Passive View patterns . At first glance, it looks very similar to ADR, especially in that the Passive View and Model have no dependencies on each other. From the text of Fowler:

The Control Controller uses the controller both to process input data and to control the presentation, allowing you to organize more complex display logic ...

Passive View achieves this by reducing the number of behaviors of UI elements to an absolute minimum due to the use of the controller, which Not only handles the reaction to user events, but also takes on all the work of updating the views. This approach allows you to focus on testing the controller, without worrying about any problems in the views.


Let's take a little more detail:

  • The model and domain are pretty much alike, as is the case with MVC.
  • The Passive View is fully consistent with neither the Action nor the Respondent ; rather, it can be seen as a response that is returned to the client.
  • The Managing Controller resembles the Responder in terms of “managing the view to achieve more complex display logic.” On the other hand, the Respondent does not interact with the Domain and does not receive input from the client, so it does not seem to be too suitable for the role of the Managing Controller .
  • Alternatively, the Control Controller could be represented as an Action , but the Action is not responsible for managing the views (i.e., the response).


In general, close, but not the same.

MVVM (Model-View-ViewModel)


MVVM is only partially similar to ADR. The model in MVVM is almost the same as the Model in MVC and the Domain in ADR. Similarly, View in MVVM is very similar to View in MVC and Responder in ADR.

However, the ViewModel is neither like the Controller in MVC nor the Action in ADR. Since ADR is a refinement of MVC, it is reasonable to assume that comparing MVVM with MVC will be similar to comparing with ADR.

For a detailed description of these differences, I advise you to read the articles by Joel Wenzel , Avtar Sing Sohi ,Rachel Appel and Niraja Bhatta .

(I had an interesting email discussion, during which they explained to me that MVVM is very similar to MVC, to which we added a View Model for interactions between a View and a Model . If this is really so, then the View Model can be used in ADR to same success as in MVC).

PAC (Presentation-Abstraction-Control)


From Wikipedia :

PAC is a hierarchical structure of agents, each of which is a triad of representation, abstraction and control part. Agents (triads) communicate with each other only through the controlling part of each of them. Another difference from MVC is that in each triad, representation and abstraction (a model in terms of MVC) are completely isolated. This approach allows you to process models and views in parallel in different threads, which leaves the user with the impression of a very quick start, since the interface (views) can be displayed even before the abstractions are fully initialized.


Not too similar to ADR.

RMR (Resource-Method-Representation)


I did not hear about RMR until ircmaxell pointed to him at Reddit .

ADR and RMR are very similar to each other, and their elements correspond to each other very precisely.

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


However, some of the nuances of RMR make me think that these two approaches are still different from each other. For example:

Thus, within the framework of an object-oriented language, we can consider http-resources (Resource) as objects with private properties and a specific set of public methods (Method), each of which corresponds to the standard HTTP method. In terms of MVC, a resource can be represented as a model with a small piece of the controller inside.


Personally, it seems to me just too much mixing of concepts. I prefer to see a clearer separation of models from actions performed on the application.

Representation is very similar to Representation in MVC, we give it a resource object and give the command to serialize it to the required output format.


Obviously, this is not true for a number of HTTP responses, such as, for example, “Not Found.” Such an answer is definitely not a representation of the requested resource.

In general, it is likely that ADR can be seen as an augmented and extended variation of RMR, in which the Resources and actions that can be performed on them are clearly divided into Domains and Actions , and where the View (i.e. generating a response) is controlled Defendant .

Models-Operations-Views-Events (MOVE)


From the original site :

Models encapsulate everything that your application knows.
Operations encapsulate everything your application does.
Views are the link between your application and the user.
Events are used to securely connect all of these elements.


This is a very interesting pattern in itself. The idea of Models and Operations seems to be very appropriate in the framework of the “Domain-Driven Design” approach.

However, I do not think that MOVE is similar to ADR, especially at this point:

Events are exactly what MOVE (and MVC) gives inversion of control, which is necessary for models to be able to update views without receiving information about which view they are updating.


In ADR, the Domain and Responder do not “update each other”. The work of the Domain is completed, and the result is transferred to the Respondent for further display to the client.

Separated Presentation


You can find several references to ADR, especially to Responder , in Separate Views . The article itself is worth reading, but Separate Views are more likely a meta-pattern describing general approaches to separating data from their presentation, rather than concrete ways to achieve this separation.

Comparison of MVC and ADR on examples

Starting point in MVC


In MVC, the directory structure for a commonplace blog system might look something like this. Note that index and read provide JSON output as an alternative, and the comment template is “partial” and also allows JSON output as an alternative.

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


And here is another type of directory structure for MVC:

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


The standard Controller class in MVC is approximately as follows. Note that there are different actions in this class of the Controller , and the methods of these actions set the response headers.

// 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 logic of the create () method can be reduced in some way by taking most of the interactions with the model to the Service Layer , but the essence remains the same - it is the Controller that is usually responsible for the response headers and content.

Take a look at ADR


For comparison, the folder structure in ADR can be organized as follows. Note that each Action has a Respondent corresponding to it .

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


A pair of Actions and Responder corresponding to the create () Controller method discussed above might look like this:

// 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();
    }
}
?>

// $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, you can find opportunities for refactoring in this code, especially in terms of working with the Domain . The main thing is that the Action does not do any work of the Respondent ; All necessary work is carried out directly by the logic of the Respondent .

You can learn an extended example of ADR code here .

Comments

Not considered requests


I got a lot of criticism because I did not include an element corresponding to the "HTTP request" in the pattern. An earlier version of this publication contained such an element and was called “Request-Action-Domain-Response”. However, as I studied MVC and similar architectural patterns further, I noticed that none of them defines an input element. In order not to get out of the general row, ADR also does not consider this element.

Not reviewed Front Controller


The pattern was created to improve the Model-Application-Controller approach , and not web applications in general. Thus, it deliberately does not address some of the elements inherent in many web applications, and in particular this applies to the Front Controller .

ADR does not describe the routing or scheduling element, nor does it describe how the Action and Responder are associated with scheduling . Routing and scheduling are most often the responsibility of the Front Controller , and there are many ways to establish interaction between the Action , the Responder and the Front Controller :

  • The action can directly call the Responder , which returns a response to the request;
  • The responder and the response may be available to the front controller that calls them directly;
  • An action can return a Responder , which is then called and returns a response that is called and sends itself;
  • etc.


The ADR pattern does not describe any elements of preliminary filtering or request validation, since they can be part of the Front Controller . Please note that, depending on the logic of preliminary filtering and validation of the request, the Action may call the Responder , or it may return its own response, or cause some additional Action as a result of the operation of its logic and so on. Likewise, the called Action can have its own set of checks, leading to the Responder being called without interacting with the Domain . The reasons for such “shortened” call chains may be:

  • Inconsistency in HTTP methods. If the routing system does not find a correspondence between the used HTTP method and the requested Action , the Front Controller may return an error response instead of redirecting the request to the Action .
  • Authentication The presence or absence of client access rights (and their validity) may affect the need to call an Action or interact with the Domain during this Action .
  • Authorization The access control system may reject a client request for a specific Action , or lead to the fact that the Action will be performed without interacting with the Domain and, possibly, will return a response on its own.
  • Content mismatch. The Front Controller , Action, or other elements involved in processing the request can check the Accept headers of the client request. A mismatch of these headers may force an Action or Domain to be excluded from the processing of the request and / or lead to early response output.
  • Validation of content. If for some reason the data of the incoming request is not considered valid, the Action may refuse to interact with the Domain and directly call the Responder to send an error message.

Alternative wording


ADR can be called a variation on the theme of the Controller and the View in the Model-View-Controller pattern , rather than an independent pattern.

Then it turns out that the Action is a variation similar to the Page Controller , and in this context, its more accurate name could be the Action Controller . In this case, it will correspond to the Controller from MVC. (Moreover, the formal description of the Page Controller states that it is a “page or action”).

Similarly, you can consider the Respondentas a variation similar to the Template View or Transform View , and then it would be wiser to call it a Response View . In this way, Responder fits in perfectly with the View element from MVC.

Despite all of the above, I believe that these alternative formulations are not as good and accurate as the description of the approach in the framework of a separate ADR pattern. For the most part, due to internal interactions between the Model and the View : in MVC, the View updates the Model , in ADR, the Responder does not updateDomain .

Obscure Domain


A domain covers a lot: not only a set of application classes, but also its state and state of the environment. Probably, it could be called a Model , but this would only add additional confusion.

In addition, it is possible that the Action should not transmit to the Respondent the data received from the Domain , but the Presentation Model . But in this case, it is possible that the Domain called by the Action should return the Model View encapsulating the state of the application.

In any case, ADR is proposed as a refinement of MVC. Based on this, we can say about the Domainin ADR, everything is the same as for Models in MVC.

Explaining Actions


One of the commenters noted that the Action can be organized so that different logic works depending on the incoming request. For example, he suggested that readers can extend the Action and add functionality for working with different HTTP methods to it, placing the logic for different HTTP methods in the same Action .

Although in my opinion the pattern itself expresses the idea that each Action should perform only one function, and this clearly follows from comparisons of the Controller and the Action and the ADR and RMR patterns, I will clarify again, in a more explicit form: meaning is that every actionmust perform one and only one action in response to any incoming request.

Replacing, not finalizing MVC


Nate Eibel objects that ADR should be considered a MVC replacement used for server applications:

I will say that the more I learn about the MVC pattern, the less I find it suitable for the server side of web applications. <...> I think the main breakthrough of your idea of ​​ADR is a decoupling from what seems to me a bad abstraction. I would advise you to avoid describing ADR in terms of MVC except where absolutely necessary.


Full comment .

Other comments


The original blog post describing this idea is here .

Stefan Hochderfer answered my idea in this post ; further discussion continued here and on reddit .

John Leighton wrote about the Focused Controller, which is very similar to the Action from ADR, here .

The next post comparing Representation and Respondent is here , and comments on it on the reddit are here and here .

Akihito Koritama offers his comments here .

Advantages and disadvantages


One comprehensive advantage is that the pattern more accurately describes the most common interaction scenarios on the web. The request comes and redirects to action; the action interacts with the domain, and after that the response is built. The response work — including both the headers and the content — is completely and completely separate from the action work.

One complex flaw is that we have to deal with the increased number of classes in the application. Not only each Action , but also each Respondent will receive its own class .

But this flaw is probably not so terrible in the long run. The need to create separate classes can lead to a clearer and less deep inheritance hierarchy. Separation of Actionsand Defendant contributes to better testability. In different systems, these features can manifest themselves in different ways. Someone noticed that it is more convenient to process “many classes” in different code editors and IDEs than “fewer classes, but more methods”, since class overview is usually simpler than method overview.


Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Object oriented programming

Terms: Object oriented programming