当我的域中的“某些东西”发生变化时,我需要在我的本地驱动器上创建一个文件夹。因此,在 DDD 方式中,我需要引发一个事件,而不是让我的域创建文件夹。
我的问题是如果我的事件失败(即文件夹创建失败)怎么办?
我是否必须重新提出另一个命令来撤消我认为称为补偿命令的第一个更改?
另外,如果补偿命令失败怎么办?现在我进行了域更改,但该文件夹不存在。
当我的域中的“某些东西”发生变化时,我需要在我的本地驱动器上创建一个文件夹。因此,在 DDD 方式中,我需要引发一个事件,而不是让我的域创建文件夹。
我的问题是如果我的事件失败(即文件夹创建失败)怎么办?
我是否必须重新提出另一个命令来撤消我认为称为补偿命令的第一个更改?
另外,如果补偿命令失败怎么办?现在我进行了域更改,但该文件夹不存在。
您描述您提出的解决方案的方式并不是真正的 DDD。我认为更多的是 CQRS(即事件和补偿命令),这可能会使情况复杂化。
对于这种用于异步操作的场景,您真的需要采用 CQRS 方法吗?例如,在单独的事务中创建的文件夹对于调用和持久化的业务逻辑有什么优势?在引发查询服务处理的事件时,这种方法有充分的理由,因为查询服务可能位于单独的物理机器上,因此需要 RPC。此外,该事件可能需要更新许多非规范化表。所以为了性能,这个过程使用异步事件模型是有意义的。但是对于创建本地文件夹,我不确定是否可以?
一种可能的方法
public class ApplicationService : IApplicationService
{
private readonly IMyAggregateRepository _myAggregateRepository;
private readonly IFolderCreationService _folderCreationService;
public ApplicationService(IMyAggregateRepository myAggregateRepository, IFolderCreationService folderCreationService)
{
_myAggregateRepository = myAggregateRepository;
_folderCreationService = folderCreationService;
}
public void SomeApplicationServiceMethod(Guid id)
{
using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
{
MyAggregate aggregate = _myAggregateRepository.GetById(id);
aggregate.SomeMethod();
_myAggregateRepository.Save(aggregate);
_folderCreationService.CreateFolder();
}
}
}
在这里,只有在工作单元的 using 语句中的所有代码都完成且没有错误时,才会将更改提交到数据库。
请注意,它不是调用文件夹服务的域服务或域实体......它是应用程序服务。我更喜欢让域服务专注于纯域逻辑。协调客户端对域、数据库和任何其他基础设施服务(例如此文件夹服务)的调用是应用程序服务的工作。
如果您认为有充分的理由使用事件模型,那么您所说的就是正确的。如果在事件处理程序中创建文件夹失败,您将不得不发出补偿命令。您需要确保此处理程序不会因设计而失败(我的意思是有问题的实体始终处于可以执行此补偿命令的状态;并且状态已恢复)。拥有涵盖所有场景的良好单元测试将有所帮助。如果设计中存在允许此补偿命令失败的缺陷,我想您将不得不通过发送电子邮件/失败通知来进行人工干预。
PS虽然不是问题的重点,但我真的建议不要创建物理文件夹,除非真的有充分的理由。以我的经验,它只会让部署/机器升级和其他事情感到头疼。显然我不知道您这样做的原因,但我真的建议您使用文档存储/数据库来代替您需要存储的任何内容。
如果文件系统文件夹对您的域至关重要,我会将文件夹的创建实现为域服务。这样,您将让实体处理所有业务规则和逻辑,如果文件夹创建失败,您的域不会处于无效状态。
将服务作为参数传递给处理逻辑的方法(双重调度模式)。
文件夹服务示例。
IFolderService
{
CreateFolder(string path);
}
实体示例。
class MyEntity
{
public void DoWork(IFolderService folderService, ...)
{
folderService.CreateFolder(...);
// do work.
}
}
工作完成后,您可以引发域事件来通知子系统。