如果业务逻辑要发送电子邮件

If business logic wants to send an email

我正在尝试实施 Uncle Bob 的简洁架构:

除了 UseCases/Interactors 之外一切正常。我需要注册一个用户,所以我需要 RegisterUser 个用例。在具体的 interactor 中,我实现了 RegisterUser 用例,它只创建 User 实体并使用 PasswordBroker 端口散列其密码。

创建用户后,我需要发送一封验证邮件。为此,我需要使用框架的组件。而且主要有两个问题。

  1. 我认为为邮件服务写端口没有意义,因为这个端口只是一个巨大的copy/paste框架服务抽象
  2. Interactor 不能在用户创建后发送邮件,因为 User 最终会在 Doctrine flush() 之后存储在控制器中。当我们发送电子邮件但用户未存储在数据库中时,存在风险。一定要一致。

在这种情况下最好的方法是什么?

我认为我们需要某种与具体用例相关的端口,该端口将在用例结束时调用并在中实现Application Layer framework 的 Mailer 和 Doctrine 可用。

这是使用框架的upside/downside。干净的架构有意将您与这样的依赖项隔离开来。这可以让事情从你下面发生变化,而不会受到这种变化的影响。它还允许您针对抽象进行测试(针对框架代码编写自动化测试总是很糟糕)。

您需要做的是决定是否值得牺牲干净的架构来利用框架的优势。这是一种平衡行为,我们都必须在 case-by-case 的基础上做出决定。框架为我们做的越多,我们得到的控制就越少,通常我们的应用程序最终的架构 "pure" 也就越少。但是,如果没有框架,我们必须编写本质上是对现有功能的浪费性重复的代码。

  1. I think it has no sense to write port for the Mail service, because this port would be just a huge copy/paste abstraction of a framework service

我会创建一个 Notification 接口,然后传递给用例交互器,类似于 EntityGatewayEntityRepository

我会以一种从具体通知机制中抽象出来的方式设计 Notification 接口,例如e-mail、信使等

Notification 看起来像这样:

public interface Notification {
    public void notifyUserRegistered(User user);
}

实现将放在外层,通常是接口适配器层。因为这是调整内层接口的层 - 因此得名。

这样的 Notification 接口可以在测试中轻松模拟,这可以使您的测试快速进行。因此,我不认为通知界面是一个巨大的 copy/paste 抽象。

  1. Interactor cannot send an email after user creation, because User finally would be stored after Doctrine flush() inside controller. There is a risk, when we've sent an email, but User wasn't stored in a DB. It must be consistent.

首先,我猜你无论如何都不能强制执行,因为发送 e-mail 不参与数据库事务。因此,您仍然会遇到一致性问题。

但您通常可以注册活动交易,以便在交易成功完成时收到通知。这意味着 Notification 实现将只注册一个在事务完成时调用的回调。然后你可以发送 e-mail.

我不知道如何在 php 中完成,也许这是关于 SO 的另一个问题。在 Java 中有几种方法,具体取决于您使用的交易 api。

对于 Spring,请查看 Transaction bound events or JEE's TransactionSynchronizationRegistry。如果 Java 开发人员阅读了这篇文章,请提一下。