📜 ⬆️ ⬇️

Action-Domain-Responder - revision of MVC for web tasks

purpose


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

ADR

Prerequisites


The term MVC is experiencing some semantic blurring of its original meaning, especially in the context of the web (see the video by Stefan Pribsh for more detailed consideration of the issue). As a means of eliminating this blurring, I offer you a description of the Action-Domain-Responder pattern, which is a refinement of the MVC concept to meet the needs of solving web-specific tasks.
')
I believe that ADR is much better suited to what we actually implement in the web development process day after day. For example, the creation of this pattern was partly inspired by the way we solve the problems of routing and dispatching, because in the case of routing and dispatching we turn not to the controller class per se, but to a specific action method in this controller class.

Another problem uncovered is 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 better than MVC, the separation of concepts for web applications.

Components


An Action is the logic that binds the Domain and the Responder . It uses input data to interact with the Domain and transmits the Domain output to the Responder .

Domain (Domain) contains the logic to control the session, application and environment data, state changes and data management, if necessary.

The Responder is the logic required to create an HTTP response or description of the response. It operates with the main content (body content), templates and views, headers, cookies, status codes and so on.

Interactions


  1. The web handler receives the request from the client and sends it to Action .
  2. The action interacts with the Domain .
  3. The action sends data to the Responder (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 the response to the client.

Comparison with MVC (Model-View-Controller)


The most popular pattern describing interactions within the web is the Model-View-Controller . Is the Action-Domain-Responder really just a Model-View-Controller disguise? You may notice that the elements of ADR are 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 GUI Architecture essay [translation here ] we can see that “you have more than one view and one controller, you have 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 MVC is used in web applications.

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

Model and Domain

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

Controller and Action

In general, most Controller classes in a system designed according to the principles of MVC contain several methods, each of which corresponds to a separate action. Since all these methods reside in one Controller , additional “wrapper” logic is also added to it to work with each of these methods, for example, hooks that are triggered 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 appropriate for the Action (for example, Slim ).

Within the framework of the same ADR, Actions are seen as separate classes or closures. In other words, each Action must be rendered into its own separate class or closure.

The action interacts with the Domain in the same way that the Controller interacts with the Model , but does not interact with the View or the template system. The action simply transfers the data to the Respondent and gives the volume to dispose of them.

Submission and Responder

In the MVC system, the Controller method typically generates a response body using a View (for example, through 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, some Controller methods may provide different response formats for the same data. Since this variability is often not supported by absolutely all methods, the logic of data presentation somehow changes from method to method, with its own conditions in each specific case.

In ADR, each Action has a corresponding Responder . When an 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 defendant fully controls the setting of headers, the choice of content types, template rendering and so on.

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

Comparison with other patterns


There are other patterns that are considered as refinement, replacement, or addition of the MVC concept. You can read this review of patterns from Derek Greer .

EBI (Entity-Boundary-Interactor)

The term EBI has several synonyms: ports and adapters, hexagonal architecture, ECB (Entity-Control-Boundary). He is described as part of Robert Martin's Pure Architecture .

EBI is a partial alternative to MVC, in which the main elements and behaviors represented by Interactor objects and Entities 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 private 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 interactions within the application better than MVC. If the description above is true, then ADR fits pretty well with the EBI structure:


Alternatively, within the terminology of ports and adapters or a hexagonal architecture, it may be more reasonable to consider the Action as a “port” through which the EBI Limiter is invoked (invoke) as part of the ADR Domain . Finally, the Respondent can be viewed 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 him the ADR supplement to the same extent.

MVP (Model-View-Presenter)

MVP was dismissed 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 Controller uses the controller both to process the input data and to control the view, allowing you to organize more complex display logic ...

Passive View achieves this by reducing the number of behaviors of UI elements to the absolute minimum by using a controller that not only processes the reaction to user events, but also takes all the work of updating the views. This approach allows you to focus efforts on testing the controller, without worrying about any problems in the views.

Let's take a closer look:


In general, close, but not the same.

MVVM (Model-View-ViewModel)

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

However, the View Model (ViewModel) is not similar to the Controller in MVC, nor to the Action in ADR. Since ADR is a refinement of MVC, it is reasonable to assume that a comparison of MVVM with MVC will be similar to that of ADR.

For a detailed description of these differences, I advise you to read the articles by Joel Venzel , Avtar Sing Soha , Rachel Appel and Niraj Bhatt .

(I had an interesting email discussion where I was told that MVVM is very similar to MVC, to which the View Model was added for interactions between View and Model . If this is true, then View Model can be used in ADRs with same success as 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 control part of each of them. Another difference from MVC is that in each triad, representation and abstraction (a model in MVC terms) 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 fast start, since the interface (views) can be displayed before the abstractions are fully initialized.

Not too much like ADR.

RMR (Resource-Method-Representation)

I did not hear about RMR until ircmaxell pointed it at Reddite .

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 stay with the opinion 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 MVC terms, a resource can be represented as a model with a small piece of the controller inside.

To me personally, this just seems like too much mixing of concepts. I prefer to see a clearer separation of the models from the 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 into 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 viewed 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 Representation (i.e. response generation) is controlled The defendant .

Models-Operations-Views-Events (MOVE)

From the original site :

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

This is a very interesting pattern in itself. The idea of Models and Operations seems to be very appropriate within 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 gives MOVE (and MVC) the inversion of control necessary for models to be able to update views without getting information about what kind of view they are updating.

In ADR, 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 indications for ADR, especially Respondent , in Separate Views . The article itself is worth reading, but Separate Views are more of a meta-pattern describing general approaches to separating data from their presentation, rather than specific ways to achieve this separation.

Comparison of MVC and ADR with examples


Starting point in MVC

In MVC, the directory structure for a banal blogging system might look something like this. Note that index and read provide as an alternative the output in JSON, and the comment template is “partial” (partial) and also allows the output of JSON 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 looks something like this. Note that in this controller class there are different actions, and the methods of these actions set 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 logic of the create () method can be reduced in some way by making most of the interactions with the model at the Service Layer , but the essence remains the same - the Controller is usually responsible for the response headers and content.

Take a look at ADR

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

 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 the Action and the Responder , corresponding to the create () method described above, could 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, you can find opportunities for refactoring in this code, especially with regard to working with the Domain . The main thing is that the Action does not do any work for the Respondent ; all necessary work is done directly by the logic of the Respondent itself.

You can view the advanced sample ADR code here .

Comments


Not considered requests

I received quite a lot of criticism for not including the element corresponding to the "HTTP request" in the pattern. An early version of this publication contained such an element and was called the "Request-Action-Domain-Response". However, in further studying MVC and similar architectural patterns, I noticed that none of them define an input element. In order not to stand out from the general row, ADR does not consider this element either.

Not considered Front Controller

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

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


The ADR pattern does not describe any elements of pre-filtering or request validation, as they may be part of the Front Controller . Note that, depending on the pre-filtering logic and the 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 its logic, and so on. Similarly, a triggered Action may have its own set of checks, resulting in a Responder calling without interacting with the Domain . The reasons for such “shortened” call chains can be:



An ADR can be called a variation on the Controller and Presentation theme 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 might be Action Controller . In this case, it will correspond to the controller from MVC. (Moreover, the formal description of a Page Controller is that it represents a “page or action”).

Similarly, the Respondent can be consideredas a variation similar to the Template View or Transform View , and then it would be wiser to call it a Response View . Thus, the Responder fits perfectly into the view element of MVC.

Notwithstanding the foregoing, I believe that these alternative formulations are not as good and accurate as the description of the approach within 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 .

Unclear Domain

The domain covers a lot: not only the application class set, but also its state and environment state. It could probably be called a Model , but that would only add further confusion.

In addition, it is possible that the Action should transfer to the Respondent not 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 that encapsulates the state of the application.

In any case, ADR is offered as a revision of MVC. On this basis, we can say aboutThe domain in ADR is all the same, what can be said about the Model in MVC.

Explaining Actions

One of the commentators noted that the Action can be organized in such a way that different logic works in it, depending on the incoming request. For example, he suggested that readers can extend the Action and add functionality to work with different HTTP methods by 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 the Controller and Action comparisons and the ADR and RMR patterns, I will clarify once again, more explicitly: the meaning is that every action must perform one and only one action in response to any incoming request.

Replacement, not revision of MVC

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

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

Full comment .

Other comments

The original blog post describing this idea is here .

Stefan Hochdurfer responded to my idea in this post ; further discussion continued here and on reddit .

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

The subsequent post comparing the Submission and the Respondent is here , and the comments on it are read here and here .

Akihito Coritama offers his comments here .

Advantages and disadvantages


One complex advantage is that the pattern more accurately describes the most typical interaction scenarios on the web. The request comes in and is redirected to the action; the action interacts with the domain, and then the response is built. The work of the response — including the headlines and the content — is entirely separate from the work of the action.

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

But this drawback 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 Actionsand Responder promotes better testability. In different systems, these features may manifest themselves in different ways. Someone noticed that it is more convenient to handle “many classes” in different code editors and IDEs than “fewer classes, but more methods”, since class overview is usually simpler than method overview.

Thanks


I express my gratitude to everyone who with their questions, comments, criticism or recommendations helped to work on this proposal. Separately, I would like to thank the following people listed without any order:

Source: https://habr.com/ru/post/260769/


All Articles