Who does the redirect in an web application implemented in Clean Architecture?

325 views
Skip to first unread message

Christian B

unread,
Sep 5, 2014, 2:20:50 AM9/5/14
to clean-code...@googlegroups.com
Hey guys,

Im trying to "login" with clean architecture, but im having some trouble.

<?php

// PSEUDO CODE

class LoginController {
   
public function loginAction() {

       
// Get the login form
        $form
= FormFactory::factory('LoginForm');

       
// Validate the form
       
if ($form->isValid($this->getRequest()->getParams()) {

           
// Do the login
            $interactor
= LoginInteractorFactory::factory();
            $interactor
->login($form->getValue('username'), $form->getValue('password');

           
// OK so here is my question, who does the redirect? In clean arch there is no return value on the interactor
           
// I have a presenter, but the Controller does not know about the presenter.
       
}
   
}
}

What i thought was, i could set the LoginController instance as a Context in the interactor so the interactor would do something like

<?php

class LoginInteractor {

   
private $context;

   
public function login($username, $password) {
        $result
= $this->authService->login($username, $password);

       
// The Context would be the Controller, which implements loginSuccessful as the redirect, good idea?????
       
if ($result) $this->context->loginSuccessful();
       
   
}
}





Dave Hunt

unread,
Sep 5, 2014, 2:38:36 AM9/5/14
to clean-code...@googlegroups.com
I'm no PHP dev but I did this recently in another language and your solution is exactly what I did.

You could have had the interactor return a result indicating success or failure to the controller, but then you have to add some logic to the controller to evaluate that result and choose to either redirect or render.

Using your approach there is no logic in the controller. It simply needs to implement success and failure methods for the interactor to call. This makes the controller so dumb that it perhaps doesn't even need testing.

You could also move your validation logic into the interactor to make your controller truly humble.

Dave
--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Christian B

unread,
Sep 5, 2014, 2:41:21 AM9/5/14
to clean-code...@googlegroups.com
This is another question i would have asked. In PHP most frameworks have a Form package which i like to use, like Zend_Form or Symfony\Form. I do not want to bind this to my interactor, even tho i could make an interface and build some kind of bridge. Still very confused about forms anyways :)
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Dave Hunt

unread,
Sep 5, 2014, 3:12:08 AM9/5/14
to clean-code...@googlegroups.com
Yes I struggled with this a little myself. Forms frameworks are helpful in that they offer you a way to declaratively state what makes a form valid or not. In other words, they take the responsibility for implementing that logic, so you don't have to.

But it's your code that must decide what to do depending on whether or not a form is valid. You should provide the result of that validation to the interactor so that it can decide what to do.

I tend to have a object implementing a UI interface passed to my interactors. This UI object contains things like the loginSuccess or loginFail methods. It also provides the username and password properties so I can pick up those details, and it would implement the isValid method.

Dave
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.

Christian B

unread,
Sep 5, 2014, 3:18:58 AM9/5/14
to clean-code...@googlegroups.com
Yea, i'll try that out thanks :)
Message has been deleted
Message has been deleted

Jakob Holderbaum

unread,
Sep 6, 2014, 1:01:42 AM9/6/14
to clean-code...@googlegroups.com
I like your answer, especially the separation between login and
authentication. I see it the same way and strive for the same abstractions.

But I don't like the idea of using an exception for failed
authentication. I am not strong on this point but I learned (and adhere
to it) that execution flows should be very rarely guided by throw/catch
interactions. Exceptions are called exceptions because, well, they are
an exception of the regular control flow.

A failing authentication attempt is probably not so rare in an web
application, or is it?

The definition of exception is very application and context specific,
but in this case I feel slightly disquiet.

Any thoughts on that?

Cheers
Jakob

On 09/05/2014 08:59 PM, Gareth Wedley wrote:
> Ive struggled with this one too.
>
> One thing I would say is do you mean login() or do you mean authenticate()?
> Login to me assumes that something is managing some sort of user session. I
> could be wrong, but I feel session management falls into the delivery
> mechanism layer. For example if you had a RESTful web service, you would
> have to authenticate every request. If you had a more traditional web
> application, you'd manage it in a cookie. Either way, it depends on the
> delivery mechanism not the application.
>
> What the application can offer you is an interactor to authenticate a user.
> Lets call it AuthenticateUserInteractor
>
> Think about the command pattern that is applied to the interactors. We will
> have a method execute() on AuthenticateUserInteractor. If the user is
> authenticated, the interactor can call some method on the presenter
> interface, something like authenticateUserSuccess(Response), and the
> execute() method returns null. This is an example of Command Query
> Separation. We commanded the class to do something. If it succeeds we
> expect it to return nothing.
>
> What about the case where a user fails authentication? Thinking about
> Command Query Separation, if we command the interactor to do something and
> it fails to carry out the command we should do the same thing that most
> code does if it fails - throw an exception. The interactor can throw a
> FailedToAuthenticateUserException.
>
> You can then wrap the call to the interactor in a try/catch.
>
>
> On Friday, 5 September 2014 07:20:50 UTC+1, Christian B wrote:
>>
>> Hey guys,
>>
>> Im trying to "login" with clean architecture, but im having some trouble.
>>
>> <?php
>>
>> // PSEUDO CODE
>>
>> class LoginController {
>> public function loginAction() {
>>
>> // Get the login form
>> $form = FormFactory::factory('LoginForm');
>>
>> // Validate the form
>> if ($form->isValid($this->getRequest()->getParams()) {
>>
>> // Do the login
>> $interactor = LoginInteractorFactory::factory();
>> $interactor->login($form->getValue('username'), $form->
>> getValue('password');
>>
>> // OK so here is my question, *who does the redirect*? In
>> clean arch there is no return value on the interactor
>> // I have a presenter, but the Controller does not know about
>> the presenter.
>> }
>> }
>> }
>>
>> What i thought was, i could set the LoginController instance as a Context
>> in the interactor so the interactor would do something like
>>
>> <?php
>>
>> class LoginInteractor {
>>
>> private $context;
>>
>> public function login($username, $password) {
>> $result = $this->authService->login($username, $password);
>>
>> // The Context would be the Controller, which implements
>> loginSuccessful as the redirect, good idea?????
>> if ($result) $this->context->loginSuccessful();
>>
>> }
>> }
>>
>>
>>
>>
>>
>>
>

--
Jakob Holderbaum, M.Sc.
Systems Engineer

0176 637 297 71
http://jakob.io
h...@jakob.io
#hldrbm

Dave Hunt

unread,
Sep 6, 2014, 4:31:59 AM9/6/14
to clean-code...@googlegroups.com
I agree with Jakob.

I wouldn't have the interactor throw an exception. There's nothing exceptional about a failed database query. It should just be another case that the interactor deals with.

I like exceptions generally. They're helpful for throwing messages way up the call stack to be handled by the function best suited to deal with it. But I never catch and re-throw.

In this case, throwing again would make the controller handle it. As I mentioned before, the controller should be as dumb as possible, and used only to wire up events and forward responses through views and other http results.

Dave
> --
> The only way to go fast is to go well.
> --- You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.

Gareth Wedley

unread,
Sep 6, 2014, 4:47:01 AM9/6/14
to clean-code...@googlegroups.com
Sorry, deleted my answer as wanted to modify slightly. Re-adding it here

Gareth Wedley

unread,
Sep 6, 2014, 5:32:15 AM9/6/14
to clean-code...@googlegroups.com
Im still caught up on the command nature of interactors. You are asking the interactor to authenticate a user. If it can't do this, it has failed. 

I agree with what Jakob says 'Exceptions are called exceptions because, well, they are an exception of the regular control flow'. But in a Command Query Separation sense, a command that has not been carried out IS an exception to the flow of control. The normal flow of control would expect all commands to be completed.

The reason an exception is the best way to manage this is that we return control back to the caller. We are saying 'hey, we've not been able to do what you commanded us to do'. The caller can catch the exception and decide what to do next. It may be that in the grand scheme of things, a command failing will not cause an issue, the controller can catch the exception and move on with any other commands it wants to perform. But the point is that it should be the controller that makes this decision to continue, not the interactor. If the interactor doesn't throw an exception and calls authenticationFailed() on the presenter, the execute() method will then return null to the controller. The controller will think this command has succeeded! It will assume a user is authenticated which may end up with sensitive information being presented to a non authenticated user! 

Łukasz Duda

unread,
Sep 6, 2014, 6:59:46 AM9/6/14
to clean-code...@googlegroups.com
Hi guys,
I can't agree ;-) In my opinion authentication is query, since it doesn't change state of the system and returns a result. It should not throw exception if user name and password don't match any user.
Should it be log in or authenticate? I can imagine log in use case that has nothing to do with HTTP session. For example it authenticates user. If authentication succeeds it resets count of authentication failures and starts session (not technical session, it's abstraction not implementation). If authentication fails it increments count of authentication failures it shows appropriate message and doesn't start session. If authentication failures count is grater than 3 it shows another message and doesn't start session.
If you use responder interface you don't return any value from execute, so it won't return null.

Gareth Wedley

unread,
Sep 6, 2014, 9:15:09 AM9/6/14
to clean-code...@googlegroups.com

'In my opinion authentication is query, since it doesn't change state of the system and returns a result.....If authentication succeeds it resets count'

Reseting a count is changing the state of the system. Therefore it is a command not a query. All interactors should contain commands. If they don't, what are you going to do with the returned result from the query? Apply some logic presumably? Logic that should not be in the delivery mechanism. 

Some people may think that the interactors do return a result as they call the presenters, but I don't think this is the right thinking. Sending a response via the presenter does not make the interactor authenticate() method a query. ALL the interactors follow the command pattern. ALL methods on interactors are commands. ALL methods return null.

When the interactors call the presenters, they too are calling commands. The interactor expects no response from the method it calls on the presenter. This is why the boundary between the delivery mechanism and the use cases is so powerful. It is a boundary where control is passed backwards and forwards through commands. It creates complete separation between the delivery mechanism and the use cases. By using command methods in this way, there is no risk of responsibility leaking from one side to the other. That is the problem with queries. You should not ask an object for something, you should tell it to do something. If you don't you risk having to perform logic with the query result that should probably belong in another class.

Łukasz Duda

unread,
Sep 7, 2014, 2:35:50 PM9/7/14
to clean-code...@googlegroups.com
".....If authentication succeeds it resets count" - my idea was, that LogInUseCase resets count if authentication succeeds, while authentication identifies user by credentials or returns kind of not authenticated user (NULL object pattern).
What I was trying to say is, that there is no single best solution to the problem. I like to think that authentication is only about verification of identity, while login is something more. I often hear from business people about logging in and rarely about authenticating. You on the other hand like the idea, that authentication means something more than verifying user's identity and that's ok. It often depends on vocabulary used by people from whom come requirements.

Greetings
Lucas

Gareth Wedley

unread,
Sep 8, 2014, 5:36:26 AM9/8/14
to clean-code...@googlegroups.com
Yeah I agree with you that its about what each use case means. I see authenticate as purely a command. Authentication either passes or fails.

On reflection, these posts have actually helped me rearrange my own thoughts. I think the use case in this case really is a login use case. As people have pointed out, login not only authenticates a user, but also changes state by resetting counts etc. The authenticate method is probably best placed on a user entity. The login use case then calls authenticate on user.

With login, I guess regardless if authentication passes or fails, the transaction must be considered completed (i.e. don't throw an exception) as the act of logging in will change some state in the system.

I think my confusion was around progressing to other use cases if authentication succeeds. My thinking was wrong here. What should happen is that the delivery mechanism never calls authenticate. If a use case required authentication, its the use case that calls authenticate on a user entity. This then means that every use case that requires authentication will require a users credentials in the request. This may seem odd, but since use cases are stateless, it makes sense to pass credentials to every use case that needs it. This is why I think session handling is part of the delivery mechanism not the use cases, as they should remain stateless.

We've strayed somewhat from the original question. So my answer to who manages the redirect is its the controller. If you draw a sequence diagram out you will see that actually control will always return to the controller. The controller calls login(). Login calls a presenter method. The presenter populates a view model and sends it to the view. All methods must return (even if they return null) so control will work its way back along the path to the controller.

Control will return to the controller when the login() method returns (null in this case as its a command). But what happens from here? Well if the command login() has returned, the controller should know that the command was a success. It will know that the use case has called any presenter methods it needs, and that the presenter has loaded a view with the view model. What i do here (and I could be wrong) is I make the controller ask the presenter for the view (a query call, not a command). Something like getHtml(). The controller can then use the html returned to build the http response.

Some could argue that this is adding too much responsibility to the controller. I am constrained by my web framework as the controllers are the gateway for http requests and also must send the http response.


Reply all
Reply to author
Forward
0 new messages