skip to Main Content

Let’s assume we have the following “Create User” scenario:

  1. Users can signup to the application using Facebook, Google+ or LinkedIn;
  2. The backend should retrieve some basic profile information in order to register the user(email, firstName and lastName);
  3. Users are registered with a “client Id” (Just adding complexity to the business rule);
  4. When a signup process is done the data should be sent to a notification topic.

I can imagine a create user request with the following structure:

{
  "clientId": "someClientId",
  "authProvider": "FACEBOOK | GOOGLE | LINKEDIN",
  "accessToken": "someAccessToken"
}

So, thinking about the registration/validation flow we would have:

  1. Check if the create user request is valid;
  2. Check if the clientId is valid;
  3. Try to retrieve the profile information from the social network api;
  4. Check if all the required profile information is filled;
  5. Check if the user exists in the database;
  6. Register the user;
  7. Send the data to the notification topic;
  8. Pass the data to the presenter.

Jumping straight to the use case, we would have a constructor like:

CreateUserUseCase(
    ApplicationClientGateway applicationClientGateway, 
    SocialNetworkGateway socialNetworkGateway,
    UserGateway userGateway,
    NotificationGateway notificationGateway,
    Presenter presenter
)

and an execute method:

execute(CreateUserRequest request)

    // validates the payload
    // something like
    if (request == null)
      presenter.setError(someError);

    // validates the clientId
    applicationClientGateway.findById(request.getClientId())    

    // retrieves the profile information
    // how to inject dinamically the implementation for
    // Facebook, Google or LinkeIn based on a request parameter?
    profile = socialNetworkGateway.findByAccessToken(request.getAccessToken());

    // checks if the user exists
    userGateway.findByEmailAndAuthProvider(profile.getEmail(), request.getAuthProvider());

    //register the user
    userGateway.insert(user);

    //sends the notification
    notificationGateway.send(user);

    // sets the result
    presenter.setResult(user);

Now, we have a constructor with lots of arguments(code smells?) and at least 5 validation steps in the execute method.

It looks like a violation of the SRP, so, how can we decompose this code in order to reduce complexity in the interactor?

2

Answers


  1. First of all, lets break this in some small steps:

    1) Related to the presenter, looks like you are intersted in give the workflow some output, right? Assuming that, maybe it will be better to return what you want from the usecase and handle this one layer above. (-1 parameter at constructor)

    2) Like the others answers are saying, looks like your usecase has a LOT of responsabilities right now. I will suggest you to break this in more then one usecase.
    Something like:

    ... Your first gateway (API)
    ..... ValidateClientId.execute();
    ..... profile = RetrieveProfile.execute();
    ..... InsertUser.execute(...)
    

    3.1) Related to inject the correct bean based on the correct social network, you can handle this logic INSIDE the gateway, not before call it. Remembet that one gateway CAN call another gateway (they are at same layer). So, I suggest you to use something like.

    At usercase -> socialNetworkGateway.findByAccessToken(…)
    Inside the gateway you can do your “switch” and call somthing like the FacebookGateway, GoogleGateway, etc.

    Login or Signup to reply.
  2. SRP does not say that you have to have small methods or only few constructor parameters. SRP says “there should be only a single reason to change the code”.

    As far as I can see you explicitly implemented the “business logic sequence” required to register a new user. Even though this may require some “external services” and/or repositories there is still only one reason to change the logic of this code: if the logic “how to register a user” changes.

    From this perspective you are not violating SRP.

    According to Uncle Bobs picture on “control flow” (https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) it is also totally correct to pass the presenter.

    If you still feel the need to reduce the dependencies of your use case class I would suggest looking into “unit of work” pattern and check whether it would make sense to combine some of the dependencies.

    More details on “What is a use case in Clean Architecture” you can find in my blog series: http://www.plainionist.net/Implementing-Clean-Architecture-UseCases/

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search