首页 > 解决方案 > Clean Architecture:编写应用业务规则时如何降低复杂度?

问题描述

假设我们有以下“创建用户”场景:

  1. 用户可以使用 Facebook、Google+ 或 LinkedIn 注册该应用程序;
  2. 后端应该检索一些基本的个人资料信息以注册用户(电子邮件、名字和姓氏);
  3. 用户使用“客户 ID”注册(只是增加了业务规则的复杂性);
  4. 注册过程完成后,应将数据发送到通知主题。

我可以想象一个具有以下结构的创建用户请求:

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

因此,考虑一下我们将拥有的注册/验证流程:

  1. 检查创建用户请求是否有效;
  2. 检查clientId是否有效;
  3. 尝试从社交网络 api 中检索个人资料信息;
  4. 检查是否填写了所有必需的个人资料信息;
  5. 检查用户是否存在于数据库中;
  6. 注册用户;
  7. 将数据发送到通知主题;
  8. 将数据传递给演示者。

直接跳到用例,我们将有一个构造函数,如:

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

和一个执行方法:

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);

现在,我们有一个带有很多参数(代码味道?)的构造函数,并且在执行方法中至少有 5 个验证步骤。

它看起来违反了 SRP,那么,我们如何分解这段代码以降低交互器的复杂性?

标签: clean-architecture

解决方案


首先,让我们分几个小步骤来打破这个:

1) 与演示者相关,看起来您有兴趣为工作流提供一些输出,对吧?假设这一点,也许最好从用例返回你想要的东西并处理上面的这一层。(构造函数中的-1参数)

2)就像其他答案所说的那样,看起来您的用例现在有很多责任。我会建议你在一个以上的用例中打破这个。就像是:

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

3.1)与基于正确的社交网络注入正确的bean相关,您可以在网关内部处理此逻辑,而不是在调用它之前。记住一个网关可以调用另一个网关(它们在同一层)。所以,我建议你使用类似的东西。

在 usercase -> socialNetworkGateway.findByAccessToken(...) 在网关内部,您可以进行“切换”并调用诸如 FacebookGateway、GoogleGateway 等之类的东西。


推荐阅读