我需要在本地驱动器上创建一个文件夹,当我的域中的“某些内容”发生变化时。因此,在DDD时尚中,我需要提出一个事件,而不是让我的域创建文件夹。如何处理DDD失败?
我的问题是如果我的事件失败(即创建文件夹失败)?
我是否必须重新提出另一个命令才能撤消第一个我认为称为补偿命令的更改?
另外如果补偿命令失败怎么办?现在我有一个域更改,但该文件夹不存在。
我需要在本地驱动器上创建一个文件夹,当我的域中的“某些内容”发生变化时。因此,在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语句的单元内完成,没有错误。
请注意,这不是一个域服务,或域实体调用该文件夹的服务......这是在应用服务。我更喜欢将域服务专注于纯域逻辑。应用服务的工作是协调客户端对域,数据库和任何其他服务(如此文件夹服务)的调用。
如果您决定使用事件模型有充分的理由,那么您所说的是正确的。如果在事件处理程序中创建文件夹失败,则必须发出补偿命令。你需要确保这个处理程序不能失败设计(我的意思是所讨论的实体总是处于可以执行补偿命令的状态,并且状态恢复)。拥有涵盖所有场景的良好单元测试将有所帮助。如果在设计中存在一个缺陷使得补偿命令失败,我想你必须通过发送失败时的电子邮件/通知来进行手动干预。
P.S.虽然不是问题的关键,但我真的不建议创建物理文件夹,除非有真正的理由。根据我的经验,这只会导致部署/机器升级和事情的头疼。显然,我不知道你这样做的理由,但我真的推荐使用文档存储/数据库来代替你需要存储的任何东西。
大卫,非常感谢你在这个辉煌的答复中的时间和精力。过去几个月我一直在看CQRS,我想我开始将它看作是一切的架构模式,而不是针对特定的子域:)。不幸的是,我正在迁移一个现有的应用程序,所以我们被困在文件夹中,但再次感谢您的建议。 –
如果文件系统文件夹对您的域至关重要,那么我将实现将文件夹创建为域服务。这样,您可以让实体处理所有业务规则和逻辑,并且如果创建文件夹失败,则您的域不会处于无效状态。
将服务作为参数传递给处理逻辑的方法(双派遣模式)。
文件夹服务示例。
IFolderService
{
CreateFolder(string path);
}
实体示例。
class MyEntity
{
public void DoWork(IFolderService folderService, ...)
{
folderService.CreateFolder(...);
// do work.
}
}
工作完成后,您可以引发域事件以通知子系统。
你是否一定要让它成为事件驱动?域代码是否可以调用服务外观(具有使用文件系统的实现)?那么你只会在失败时抛出异常,域代码将处理异常。 – David
感谢大卫的回复。我可以但我试图进入DDD的做事方式,谈论事件的提出。 –
此外,如果还有其他需要通知的子系统,则使用DDD /事件驱动方法,那么为它们创建事件处理程序应该比处理逻辑的域更容易工作。 –