I am trying to organize my architecture following the clean architecture while using some common practices/frameworks. I'm writing in PHP, but have no problem with other languages. The flow of my app is somewhat like this:

  1. I got a front controller/bootstrapper which instantiates an App object.
  2. Inside de App I've got a router, which instantiates a Controller and then it calls an Action method.
  3. Controller Action gets a Use Case object as parameter using DI and returns raw data: boolean, string, array or whatever.

My doubt is: Where should I go from here? I have to build a (Psr) Response (loading views, redirecting or whatever) and send it to browser. Where this kind of logic should reside?

Let's say I have a common User Crud: A list of users is shown on screen and there's a "Create User" Button, which links to a Create User form. If validation fails on this form, the form is shown again showing why it was rejected. If everything is OK, then the user is redirected to the "Users list" again. After I execute the Use Case on controller, should I test the return and build the Response (render views and/or call redirect to other screen or print a JSON if it's an API method) right in the Controller Action? It just seems wrong. I have read a lot of stuff about SOLID, Clean Architecture, Separation of Concerns and good practices and I truly could not find anything addressing this kind of thing.

I appreciate if someone can shed some light on my thoughts. I also apologize for this long post and for possible grammar errors, because English is not my native language.

  • 1
    Sorry, which option seems wrong? And why? – candied_orange Jan 23 at 16:42
  • What seems is wrong to me is the process of building an Response object on a controller action. Let's say the "Create User Use Case" I mentioned earlier returned "ok". Then, on the Controller Action that called the use case, I must build a "response redirect" to make the user go to the "List of Users" screen. But if the "Create Use Use Case" return "error", I must load the form with the user sent plus the validation errors. All this decision making on a Controller Action really seems wrong. Thank you for asking. – Erick de Azevedo Lima Jan 23 at 20:28
  • Not sure what you mean by controller action but the controller doesn’t build the response data structure. The use case does. – candied_orange Jan 23 at 20:31
  • Don't have time to write the full answer, but maybe this will help. In a browser client - remote server scenario, you're essentially making two applications - think of everything executing on the browser side (HTML, JS) as the frontend app, with server-side php being the backend. You have to treat the frontend as untrustworthy anyway (e.g. anyone using it can alter requests). Then a "view" on the backend is essentially a part of the the frontend that you "render" into HTML and send over the network. – Filip Milovanović Jan 23 at 20:53
  • If the client side is not represented just by thin views (little to no behavior), then you might want to separate out the client side stuff, or consider using some frontend framework, and have PHP just render JSON responses. – Filip Milovanović Jan 23 at 20:53

After I execute the Use Case on controller, should I test the return and build the Response(render views and/or call redirect to other screen or print a json if it's an api method) right in the Controller Action?

In Clean Architecture the Use Case doesn't return to the Controller. Take a look at this:

enter image description here

I know here Uncle Bob has named it Interactor instead of Use Case but it's the same thing.

Anyway, in Clean Architecture after the Controller calls the Use Case (or Interactor, or Application Business Rule, or whatever the hell you want to call it) the Controller is done. It wont do anything again until another request comes in.

@candied_orange. In order to build the Response, the Use Case must be aware of outer layers, which is not good. - Erick de Azevedo Lima

enter image description here

The Use Case knows about the Request Model, Entities, and it knows about the interfaces within it's own layer. Behind which might be hiding a repository abstracting a file system, or database, or your streaming service of cat videos. The Use Case can use all of these to build the Response Model (or, damnit - stick with a name, Output Data). Once it's ready it calls out to the presenter which deals with sending this out over the web, or to a console, or makes a new streaming cat video with it.

Here's a more complete diagram from his Clean Architecture book. I've animated the flow of control:

enter image description here

All of that conforms to the Clean Architecture object diagram. There are no cyclic dependencies. What knows about what is carefully controlled. Yet the flow of control is able to go everywhere it's needed.

enter image description here

The secret to making that work is right there in the lower right corner. It's parts have been renamed (again!), folded over, and flipped to make a plug in shape but that's the good old Dependency Inversion Principle:

enter image description here

That's how you can dive into lower layers and come back out without letting the lower layers know what they're talking to. Using return is not required.

Of course none of this works until you build this object graph. I go into doing that here and here.

  • Thank you for your detailed answer. I took the time to completely read it at least 3 times. I followed all of the links and have read it all as well. The example "main" function you put in one [link]comment you put in this answer helped me craft a first draft of my Proof-of-Concept-App flow. But first, I'm gonna show you the flow using steps: !Draft of App Control Flow – Erick de Azevedo Lima Jan 25 at 16:21
  • @ErickdeAzevedoLima looks good. Just so you know, the presenter doesn’t have to construct the view. Main can do that – candied_orange Jan 25 at 17:05
  • You are right. Everything is constructed at main. As a former C/C++ developer, I like the main concept. Thank you! – Erick de Azevedo Lima Jan 25 at 17:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.