Clean Architecture:编写应用程序业务规则时如何降低复杂度?

Clean Architecture: How to reduce complexity when writing application business rules?

假设我们有以下 "Create User" 场景:

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

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

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

因此,考虑 registration/validation 流程,我们将拥有:

  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,那么,我们如何分解这段代码以降低交互器的复杂性?

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

1) 与演示者有关,您似乎有兴趣为工作流程提供一些输出,对吗?假设那样,也许 return 你想从用例中得到什么并处理上面的这一层会更好。 (构造函数中的 -1 个参数)

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

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

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

在用户案例中 -> socialNetworkGateway.findByAccessToken(...) 在网关内,您可以执行 "switch" 并调用 FacebookGateway、GoogleGateway 等

SRP 并没有说你必须有小方法或只有几个构造函数参数。 SRP 表示 "there should be only a single reason to change the code".

据我所知,您明确实施了注册新用户所需的 "business logic sequence"。尽管这可能需要一些 "external services" and/or 存储库,但仍然只有一个原因需要更改此代码的逻辑:如果逻辑 "how to register a user" 更改。

从这个角度来看,您没有违反 SRP。

根据鲍勃大叔在"control flow"(https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)上的图片,通过主持人也是完全正确的。

如果您仍然觉得需要减少用例的依赖性 class 我建议您研究 "unit of work" 模式并检查组合某些依赖性是否有意义。

有关 "What is a use case in Clean Architecture" 的更多详细信息,您可以在我的博客系列中找到:http://www.plainionist.net/Implementing-Clean-Architecture-UseCases/