干净的体系结构:编写应用程序业务规则时如何降低复杂性?

时间:2019-01-22 23:21:32

标签: clean-architecture

假设我们具有以下“创建用户”方案:

  1. 用户可以使用Facebook,Google +或LinkedIn来注册该应用程序;
  2. 后端应检索一些基本的配置文件信息以注册用户(电子邮件,firstName和lastName);
  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方法:

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

现在,我们有一个构造函数,其中包含很多参数(代码有气味吗?),并且execute方法中至少有5个验证步骤。

这看起来像是违反了SRP,因此,我们如何分解该代码以减少交互器的复杂性?

2 个答案:

答案 0 :(得分:2)

首先,让我们以一些小步骤打破这一点:

1)与主持人有关,看起来您很喜欢为工作流提供一些输出,对吗?假设这样做,可能最好从用例中返回所需内容并处理上面的这一层。 (构造函数的-1参数)

2)就像其他答案所说的那样,您的用例现在似乎有很多职责。我建议您在一个以上的用例中打破这一点。 像这样:

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

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

在用户情况下-> socialNetworkGateway.findByAccessToken(...) 在网关内,您可以进行“切换”并呼叫诸如FacebookGateway,GoogleGateway等之类的东西。

答案 1 :(得分:2)

SRP并不是说您必须有小的方法或只有几个构造函数参数。 SRP表示“更改代码应该只有一个原因”。

据我所知,您明确实现了注册新用户所需的“业务逻辑序列”。即使这可能需要一些“外部服务”和/或存储库,仍然只有一个理由更改此代码的逻辑:如果“如何注册用户”的逻辑发生了变化。

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

根据鲍勃叔叔在“控制流”(https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)上的照片,通过演示者也是完全正确的。

如果您仍然需要减少用例类的依赖关系,我建议您研究“工作单元”模式,并检查组合某些依赖关系是否有意义。

您可以在我的博客系列中找到有关“清洁架构中的用例是什么”的更多详细信息:http://www.plainionist.net/Implementing-Clean-Architecture-UseCases/