Refactoring To Action-Domain-Responder

The v1 version of the Aura framework includes a controller to handle web assets. The idea for this controller was that an Aura package might have images, scripts, and stylesheets that need to be publicly available, but in development you don’t necessarily want to copy them to a public document root every time you change them. The framework dispatches all “/asset/*” routes to the asset controller, which in turn reads the requested package asset from the file system and places its contents into the response body. Performance-wise this is horrible, so in a production environment one would use a build process to copy all the package assets to a static asset server, but in a local development environment it is a valuable convenience.

Take a look at the v1 version of the asset controller. It is constructed as a Page Controller within an MVC architecture. The default actionIndex() method receives an Aura package name in the form of Vendor.Package and a trailing file path indicating the asset to load from that package, then reads that file from the package and loads it into the response body.

That v1 version is a mess. The Controller handles the response-building entirely, and there is no Model separation at all. Let’s try refactoring it to an Action-Domain-Responder architecture and clean it up some for a v2 version. (For this example refactoring, we have Hari KT to thank for getting us started.)

  1. First, we need to extract the Domain portions of the code. After some discussion, we determined that the Domain here is the file-reading portions of the code. Instead of an Aura-specific Vendor.Package algorithm, we build a map of vendor/package keys that point to arbitrary directory prefixes (typically but not necessarily in a Composer installation). Finally, we figure that the caching elements would be better as part of a build process rather than on-the-fly, so we remove those caching elements; this reduces a significant portion of the Domain work.

  2. Next, we extract the response-building activity to a separate Responder class. The response-building work turns out to be relatively straightforward: if the asset has a path, that means the service found it, and we should present it as 200 OK; if not, we present it as 404 NOT FOUND.

  3. Last, we rename the Controller to an Action, and name its one-and-only “main” entry point as __invoke(). We modify the code in the Action to (1) invoke the Domain with the incoming request input, (2) place the Domain data into the Responder, and (3) return the Responder.

The end result is three classes instead of one: AssetService to handle Domain work, AssetResponder to handle the response presentation, and AssetAction to handle the incoming request and pass data from the Domain to the Responder.

Compared to the original Controller class, we clearly have more classes, and (aside from the fact that we removed the caching functionality) we likely have somewhat more code as well. But each class, and each method in each class, is relatively short, and the package overall is much more testable:

  • the AssetServiceTest is completely freed up from the Action and Responder (as it should have been in the original MVC code)

  • the AssetResponderTest does not need either the AssetAction or the AssetService, and is able to examine both the body and the headers of the response

  • the AssetActionTest does little more than to check if __invoke() returns a Responder, and see if the assigned data was retained

This separation has the effect of making the underlying components much more independent of each other and a lot easier to test. If we wanted to get really serious we would use interfaces and test doubles to fully isolate the classes.

Right now, some readers are looking at this example and wondering “How is this different from refactoring to a better-separated MVC?” The main difference in this particular example is that, in a webbish MVC setup, the work of setting response headers is generally handled in the Controller. Doing so does not give us as clean a Separated Presentation as we see under ADR. (Remember: on the web, the template is not the view; the response is the view.) In addition, to test the full response (i.e., the headers as well as the body) we would need to run the controller action code instead of just the separated presentation code.

Two final notes:

  • This example is not the only way to do Action-Domain-Responder. The Action could invoke the Responder directly, instead of allowing the calling code to invoke it. The Action might receive a ResponderFactory instead of a Responder object directly. The Responder might receive a ResponseFactory instead of a Response object directly. The Action might just be a closure in a micro-framework route. The point is that we now have a cleanly separated presentation, where the response-building work is completely extracted from the Action, and the Domain work is simiarly completely extracted from the

  • The Action returns a Responder and not a response object; this is predicated on how Aura.Dispatcher works. When the dispatcher invokes a Controller or Action, it checks the return value from that invocation; if that return value is itself invokable, the dispatcher does so recursively until the return result is no longer invokable. This means that the Dispatcher becomes responsible for invoking the Responder returned from the Action; the invoked Responder returns the completed response object.)

That is all; if you have comments on the Action-Domain-Responder paper, please leave them here or as issues out at Github.

Are you stuck with a legacy PHP application? You should buy my book because it gives you a step-by-step guide to improving your codebase, all while keeping it running the whole time.

25 thoughts on “Refactoring To Action-Domain-Responder

  1. > “The Action returns a Responder and not a response object;”

    I like this part. The action result can be retrieved with the method “getData()”. Good for testability. I also wonder if it is possible, Action calls on another action to use this result ?

  2. After reading Matthew’s Facade post I am thinking more clearly now that it is the outcome of calling the action method that should implement the HTTP Response interface. This would be the action method’s SRP. However since the action method is a Facade to a bag of nuts and bolts, eaxctly how the HTTP Response is created does not matter, it is up to the action method to decide how to create the response.

    Interoperably speaking it is the HTTP Response object that should have a mutual interface between the domains. I realize more now that the behaviour of the Responder object is best suited to Aura’s recursive dispatch method, which breaks the SRP of calling the action method.

    The reason that the action method should satisfy its SRP is that it then becomes self contained. For example, since the action method of the Asset bundle returns a Responder which then needs to be called in order to return the HTTP Response, means that the Asset bundle is primarily limited to the Aura framework or one that uses a recursive dispatcher. Whereas returning a HTTP Response should mean that the Asset bundle is self contained and interoperable with any calling code that satisfies the interface of the signatures of the action method’s arguments.

    It would be possible to still use a Responder object, but it would need to be contained within the bounds of the action method. But since purity is not always an option, the Responder object fits in very well with Aura’s dispatcher, and it may not be achievable elsewhere either!

    Just my thoughts on the discussion of interfaces…

    • > It would be possible to still use a Responder object, but it would need to be contained within the bounds of the action method.

      This fits within the definition of ADR. As long as it is the Responder, and not the Action, that creates/manipulates/returns the response object, the pattern is satisfied. The last line of code in the Action, for example, might well be return $this->responder->__invoke().

      For more on this, see these ending notes in the pattern paper:

  3. My interpretation of Matthew’s Facade description means that it is not about a literal action method. Rather, its about the action “call” that is bound to the HTTP Response SRP. So a series of methods can also constitute as an action method, and how they inter-communicate could be with a Responder object, but then one of those grouped methods would need to call the Responder. There is already an event object, which is more like a message object than a type of service object.

    One bits that niggles me is that the Responder has to do something in order to get the response, my knee jerk reaction is that it should be the response or be able to directly return it, i.e its a message or representation of state.

    I was also wondering whether the Asset object could be created via DI with its request params and injected into the action class along with the HTTP Response object.

    • > One bits that niggles me is that the Responder has to do something in order to get the response

      By way of analogy, would it bother you that a View has to something in order to get the template output?

  4. No, I was tired and View is too general for me to guess at what implementation you’re thinking about, otherwise yes the “View”, whatever that is, can return the output of a template. Here I am likening that to an Action returning a HTTP Response.

    I had similar thoughts when working through some code in ZF2 and returning ViewModels, and whether there could just be a Response type object. However, if I recall, I think it ended up being a mute point because when you have a series of methods being executed with a common (Event) object parameter, you only have to inspect the event object to determine its state.

    In respect to Aura’s dispatcher I find no fault (worth mentioning) with the callback proposal of the Responder, it fits in very well. Its just when trying to put it into a more familiar context, the pattern doesn’t fit as well with other styles of implementation.

    Actually, recalling more, in ZF2 (or somewhere) it is possible return a ViewModel from an Action. But this causes another Listener to occur prior to the Response, inorder to resolve the ViewModel into a HTTP Response. This would not be any different than if it was a Responder object – I suppose. But if we were to stipulate the HTTP Response from an action then it should be the Action’s responsibilty, and the top level methods would just be Route, Action, Response.

    • > Here I am likening that to an Action returning a HTTP Response.

      /me nods

      My argument here is that it’s perfectly fine for an Action to return a response, *as long as* the Action invokes a Responder to build and manipulate that response. The Action can then return the response generated by the Responder.

  5. Simply invoking the Web kernel is an action.

    An event system manages workflows and invocations, it is the behavior of its configured events and listeners that implements the MVC pattern. Once it is broken down to a series of invokable functions, it would not matter which function is the main action method; the last one calls the Responder’s invoke method.

    If separation of concerns is applied to the Responder object, it separates into an event class and a listener.

    • > If separation of concerns is applied to the Responder object, it separates into an event class and a listener.

      Which is also just fine; in terms of the pattern, Responder is a concern, not necessarily an object (although “as an object” is probably the easiest way to visualize it and not mix other concerns). As long as the concern of Responder is separated from the Action and the Domain, all is well.

  6. Hi Paul, both the AssetResponderTest and the AssetActionTest point to the same file in Github (the action test one), if you might want to have a look.

Leave a Reply

Your email address will not be published. Required fields are marked *