Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use case containing the presenter or returning data?

Considering the Clean Architecture definition, and especially the little flow diagram describing relationships between a controller, a use case interactor, and a presenter, I'm not sure if I correctly understand what the "Use Case Output Port" should be.

Clean architecture, like ports/adapters architecture, distinguishes between primary ports (methods) and secondary ports (interfaces to be implemented by adapters). Following the communication flow, I expect the "Use Case Input Port" to be a primary port (thus, just a method), and the "Use Case Output Port" an interface to be implemented, perhaps a constructor argument taking the actual adapter, so that the interactor can use it.

To make a code example, this could be the controller code:

Presenter presenter = new Presenter();
Repository repository = new Repository();
UseCase useCase = new UseCase(presenter, repository);
useCase->doSomething();

The presenter interface:

// Use Case Output Port
interface Presenter
{
    public void present(Data data);
}

Finally, the interactor itself:

class UseCase
{
    private Repository repository;
    private Presenter presenter;

    public UseCase(Repository repository, Presenter presenter)
    {
        this.repository = repository;
        this.presenter = presenter;
    }

    // Use Case Input Port
    public void doSomething()
    {
        Data data = this.repository.getData();
        this.presenter.present(data);
    }
}

This interpretation seems to be confirmed by the aforementioned diagram itself, where the relation between the controller and the input port is represented by a solid arrow with a "sharp" head (UML for "association", meaning "has a", where the controller "has a" use case), while the relation between the presenter and the output port is represented by a solid arrow with a "white" head (UML for "inheritance", which is not the one for "implementation", but probably that's the meaning anyway).

However, my problem with this approach is that the use case must take care of the presentation itself. Now, I see that the purpose of the Presenter interface is to be abstract enough to represent several different types of presenters (GUI, Web, CLI, etc.), and that it really just means "output", which is something a use case might very well have, but still I'm not totally confident with it.

Now, looking around the Web for applications of the clean architecture, I seem to only find people interpreting the output port as a method returning some DTO. This would be something like:

Repository repository = new Repository();
UseCase useCase = new UseCase(repository);
Data data = useCase.getData();
Presenter presenter = new Presenter();
presenter.present(data);

// I'm omitting the changes to the classes, which are fairly obvious

This is attractive because we're moving the responsibility of "calling" the presentation out of the use case, so the use case doesn't concern itself with knowing what to do with the data anymore, rather just with providing the data. Also, in this case we're still not breaking the dependency rule, because the use case still doesn't know anything about the outer layer.

However, the use case doesn't control the moment when the actual presentation is performed anymore (which may be useful, for example to do additional stuff at that moment, like logging, or to abort it altogether if necessary). Also, notice that we lost the Use Case Input Port, because now the controller is only using the getData() method (which is our new output port). Furthermore, it looks to me that we're breaking the "tell, don't ask" principle here, because we're asking the interactor for some data to do something with it, rather than telling it to do the actual thing in the first place.

So, is any of these two alternatives the "correct" interpretation of the Use Case Output Port according to the Clean Architecture? Are they both viable?

In this answer to another question, Robert Martin describes exactly a use case where the interactor calls the presenter upon a read request. No mention of MVC, MVVC, etc. can be found, so I guess that the Clean Architecture doesn't play very well with MVC in general?

Clicking on the map causes either the placePinController to be invoked. It gathers the location of the click, and any other contextual data, constructs a placePinRequest data structure and passes it to the PlacePinInteractor which checks the location of the pin, validates it if necessary, create a Place entity to record the pin, constructs a EditPlaceReponse object and passes it to the EditPlacePresenter which brings up the place editor screen.

A possible explanation would be that the application logic that traditionally would go into the controller, here is moved to the interactor, because we don't want any application logic to leak outside the application layer. Thus, here the model is not calling the presenter, because the interactor is not the model, but rather the actual implementation of the controller. The model is just the data structure being passed around. This seems to be confirmed by:

The software in this layer is a set of adapters that convert data from the format most convenient for the use cases and entities, to the format most convenient for some external agency such as the Database or the Web.

From the original article, talking about Interface Adapters. Since the controller must just be a thin adapter converting one data format to another, it must not contain any application logic, which is thus moved to the interactor.

like image 454
swahnee Avatar asked Aug 28 '17 14:08

swahnee


People also ask

What is a use case Interactor?

In Clean Architecture "use case" and "interactor" means the same: it is the component which contains business logic. The presenter in this architecture does not contain any business logic.

What is presenter in Clean architecture?

Presenter is the mediator and directs the information to either Interactor or Router. You do not need Reactive Programming like RxSwift in Clean Architecture. Complex business logic can be added into Worker that is invoked from Interactor. Clean Architecture is an unidirectional flow of controls.

What is use case Clean architecture?

In Clean Architecture "use case" (or "use case interactor") refers to the class/component which contains the business rules/business logic. The Dependency Rule defines that the use case (the business logic) should not have (compile) dependencies to the view, any external framework or IO.

What are use cases in Android?

Use cases provide a way to cut the system vertically through the horizontal layers of the system. Each use case utilizes some UI, domain logic, data logic or whatever the system is made from. A use case object, also known as Interactor , encapsulates and implements use cases of the system.

Should the use case call the presenter or the controller?

I'm strongly in favor of the use case calling the presenter directly, as this means the use case response model is "not traipsing through the controller" as stated above. However, the approach where the controller plays traffic cop with the presenter and yanks state out of the presenter, etc, is awkward.

Is it possible to call the presenter from an interface?

While not common, but it is possible that the use case may need to call the presenter. In that case instead of holding the concrete reference of the presenter, it is advisable to consider an interface (or abstract class) as the reference point (which should be initialized in run time via dependency injection).

Why should I return the usecaseresponse?

The main reason why I prefer returning the UseCaseResponse is that this approach keeps my use cases flexible, allowing both composition between them and genericity (generalization and specific generation). A basic example:

What is the purpose of a presenter in a react app?

" The purpose of the presenter is to decouple the use cases from the format of the UI. In your example, the $response variable is created by the interactor, but is used by the view. This couples the interactor to the view.


1 Answers

In a discussion related to your question, Uncle Bob explains the purpose of the presenter in his Clean Architecture:

Given this code sample:

namespace Some\Controller;

class UserController extends Controller {
    public function registerAction() {
        // Build the Request object
        $request = new RegisterRequest();
        $request->name = $this->getRequest()->get('username');
        $request->pass = $this->getRequest()->get('password');

        // Build the Interactor
        $usecase = new RegisterUser();

        // Execute the Interactors method and retrieve the response
        $response = $usecase->register($request);

        // Pass the result to the view
        $this->render(
            '/user/registration/template.html.twig', 
            array('id' =>  $response->getId()
        );
    }
}

Uncle Bob said this:

"The purpose of the presenter is to decouple the use cases from the format of the UI. In your example, the $response variable is created by the interactor, but is used by the view. This couples the interactor to the view. For example, let's say that one of the fields in the $response object is a date. That field would be a binary date object that could be rendered in many different date formats. The wants a very specific date format, perhaps DD/MM/YYYY. Whose responsibility is it to create the format? If the interactor creates that format, then it knows too much about the View. But if the view takes the binary date object then it knows too much about the interactor.

"The presenter's job is to take the data from the response object and format it for the View. Neither the view nor the interactor know about each other's formats."

--- Uncle Bob

Given that answer of Uncle Bob, I think it doesn't matter that much whether we do option #1 (let interactor use presenter)...

class UseCase
{
    private Presenter presenter;
    private Repository repository;

    public UseCase(Repository repository, Presenter presenter)
    {
        this.presenter = presenter;
        this.repository = repository;
    }

    public void Execute(Request request)
    {
        ...
        Response response = new Response() {...}
        this.presenter.Show(response);
    }
}

... or we do option #2 (let interactor return response, create a presenter inside the controller, then pass response to presenter)...

class Controller
{
    public void ExecuteUseCase(Data data)
    {
        Request request = ...
        UseCase useCase = new UseCase(repository);
        Response response = useCase.Execute(request);
        Presenter presenter = new Presenter();
        presenter.Show(response);
    }
}

Personally, I prefer option #1 because I want to be able control inside the interactor when to show data and error messages, like this example below:

class UseCase
{
    private Presenter presenter;
    private Repository repository;

    public UseCase(Repository repository, Presenter presenter)
    {
        this.presenter = presenter;
        this.repository = repository;
    }

    public void Execute(Request request)
    {
        if (<invalid request>) 
        {
            this.presenter.ShowError("...");
            return;
        }

        if (<there is another error>) 
        {
            this.presenter.ShowError("another error...");
            return;
        }

        ...
        Response response = new Response() {...}
        this.presenter.Show(response);
    }
}

... I want to be able to do these if/else that are related to presentation inside the interactor and not outside the interactor.

If on the other hand we do option #2, we would have to store the error message(s) in the response object, return that response object from the interactor to the controller, and make the controller parse the response object...

class UseCase
{
    public Response Execute(Request request)
    {
        Response response = new Response();
        if (<invalid request>) 
        {
            response.AddError("...");
        }

        if (<there is another error>) 
        {
            response.AddError("another error...");
        }

        if (response.HasNoErrors)
        {
            response.Whatever = ...
        }

        ...
        return response;
    }
}
class Controller
{
    private UseCase useCase;

    public Controller(UseCase useCase)
    {
        this.useCase = useCase;
    }

    public void ExecuteUseCase(Data data)
    {
        Request request = new Request() 
        {
            Whatever = data.whatever,
        };
        Response response = useCase.Execute(request);
        Presenter presenter = new Presenter();
        if (response.ErrorMessages.Count > 0)
        {
            if (response.ErrorMessages.Contains(<invalid request>))
            {
                presenter.ShowError("...");
            }
            else if (response.ErrorMessages.Contains("another error")
            {
                presenter.ShowError("another error...");
            }
        }
        else
        {
            presenter.Show(response);
        }
    }
}

I don't like parsing response data for errors inside the controller because if we do that we are doing redundant work --- if we change something in the interactor, we also have to change something in the controller.

Also, if we later decide to reuse our interactor to present data using the console, for example, we have to remember to copy-paste all those if/else in the controller of our console app.

// in the controller for our console app
if (response.ErrorMessages.Count > 0)
{
    if (response.ErrorMessages.Contains(<invalid request>))
    {
        presenterForConsole.ShowError("...");
    }
    else if (response.ErrorMessages.Contains("another error")
    {
        presenterForConsole.ShowError("another error...");
    }
}
else
{
    presenterForConsole.Present(response);
}

If we use option #1 we will have this if/else only in one place: the interactor.


If you are using ASP.NET MVC (or other similar MVC frameworks), option #2 is the easier way to go.

But we can still do option #1 in that kind of environment. Here is an example of doing option #1 in ASP.NET MVC:

(Notice that we need to have public IActionResult Result in the presenter of our ASP.NET MVC app)

class UseCase
{
    private Repository repository;

    public UseCase(Repository repository)
    {
        this.repository = repository;
    }

    public void Execute(Request request, Presenter presenter)
    {
        if (<invalid request>) 
        {
            this.presenter.ShowError("...");
            return;
        }

        if (<there is another error>) 
        {
            this.presenter.ShowError("another error...");
            return;
        }

        ...
        Response response = new Response() {...}
        this.presenter.Show(response);
    }
}
// controller for ASP.NET app

class AspNetController
{
    private UseCase useCase;

    public AspNetController(UseCase useCase)
    {
        this.useCase = useCase;
    }

    [HttpPost("dosomething")]
    public void ExecuteUseCase(Data data)
    {
        Request request = new Request() 
        {
            Whatever = data.whatever,
        };
        var presenter = new AspNetPresenter();
        useCase.Execute(request, presenter);
        return presenter.Result;
    }
}
// presenter for ASP.NET app

public class AspNetPresenter
{
    public IActionResult Result { get; private set; }

    public AspNetPresenter(...)
    {
    }

    public async void Show(Response response)
    {
        Result = new OkObjectResult(new { });
    }

    public void ShowError(string errorMessage)
    {
        Result = new BadRequestObjectResult(errorMessage);
    }
}

(Notice that we need to have public IActionResult Result in the presenter of our ASP.NET MVC app)

If we decide to create another app for the console, we can reuse the UseCase above and create just the Controller and Presenter for the console:

// controller for console app

class ConsoleController
{    
    public void ExecuteUseCase(Data data)
    {
        Request request = new Request() 
        {
            Whatever = data.whatever,
        };
        var presenter = new ConsolePresenter();
        useCase.Execute(request, presenter);
    }
}
// presenter for console app

public class ConsolePresenter
{
    public ConsolePresenter(...)
    {
    }

    public async void Show(Response response)
    {
        // write response to console
    }

    public void ShowError(string errorMessage)
    {
        Console.WriteLine("Error: " + errorMessage);
    }
}

(Notice that we DO NOT HAVE public IActionResult Result in the presenter of our console app)

like image 138
Jeremiah Flaga Avatar answered Sep 19 '22 02:09

Jeremiah Flaga