如何在相同的受限上下文中将查询数据库与命令数据库同步?

时间:2019-06-09 08:36:51

标签: c# domain-driven-design cqrs

我最近使用DDD Architecture开始了一个新项目。我有2个单独的数据库用于相同的有界上下文。我知道,不同的有界上下文通过消息队列互相交谈。但是由于我在相同的受限上下文中具有查询和命令功能,所以我不知道如何在相同的受限上下文中同步读取模型和写入模型。

我将NH用于写(命令)端,将EF用于读(查询)端。我将工作单元用于NH,并用工作单元修饰了我所有的命令,并使其按汇总进行事务处理:

public class NhUnitOfWork : IUnitOfWork
{
    private readonly ISession _session;
    public NhUnitOfWork(ISession session)
    {
        _session = session;
    }

    public void Begin()
    {
        _session.Transaction.Begin(IsolationLevel.ReadCommitted);
    }

    public void Commit()
    {
        _session.Transaction.Commit();
    }

    public void Rollback()
    {
        _session.Transaction.Rollback();
    }
}

和事务装饰器:

public class TransactionalCommandHandlerDecorator<T>:ICommandHandler<T>
{
    private ICommandHandler<T> _commandHandler;
    private IUnitOfWork _unitOfWork;

    public TransactionalCommandHandlerDecorator(ICommandHandler<T> commandHandler, IUnitOfWork unitOfWork)
    {
        _commandHandler = commandHandler;
        _unitOfWork = unitOfWork;
    }

    public void Handle(T command)
    {
        _unitOfWork.Begin();
        try
        {
            _commandHandler.Handle(command);
            _unitOfWork.Commit();
        }
        catch (Exception exp)
        {
            _unitOfWork.Rollback();
            throw;
        }

    }
}

因此,在应用程序层中,我必须在创建命令中协调业务流程:

public class CategoryCommandHandlers:ICommandHandler<CreateCategoryCommand>
{
    private readonly ICategoryRepository _repository;
    private readonly ICategoryQueryService _queryService;
    public CategoryCommandHandlers(ICategoryRepository repository, ICategoryQueryService queryService)
    {
        _repository = repository;
        _queryService = queryService;
    }

    public void Handle(CreateCategoryCommand command)
    {
        var categoryId = new CategoryId(Guid.NewGuid());
        var parentId=new CategoryId(command.ParentId);
        var category=new Category(categoryId,command.Name,parentId);

        var parent = _repository.GetById(parentId);

        if (parent==null)
            throw new ParentCategoryNotFoundException();

        _repository.Create(category);

        var queryModel = new CategoryQuery(categoryId.Value, category.Name, parentId.Value);
        _queryService.Create(queryModel);
    }

}

但是我不知道这是什么问题。一切运行都没有一个错误,但我已将模型保存到Query数据库中并编写了模型主体。

如果我应该为读取模型使用单独的事务装饰器,如何确定正确运行两个事务而没有错误?还是回滚(如果发现错误)?

也许命令完全错误,并且我不知道如何处理和同步读取和写入!

1 个答案:

答案 0 :(得分:0)

我对NH,EF和事务管理的了解还不够,所以这将是一个部分答案,试图解决您有关将单独的事务用于读写的问题。

  1. 通常,出于以下两个原因,您可能希望将查询模型的构建机制与命令事务分开:

    a。查询模型往往很庞大

    b。根据需求可能会构建多个查询模型,这些查询模型通常是由不同的UI屏幕或视图模型决定的

  2. 第二,要确保事务在查询模型持久化过程中不会失败,请在命令模型事务中预先进行所有验证。查询模型的构造应是基本的收集和序列化活动类型,该活动将以所需格式生成文档。它不应该涉及验证。

  3. 使用相同的消息排队机制将查询模型构造作为异步作业运行,这没有错。通常,查询模型是处理命令后生成的众多事物之一。一个很好的机制是在命令处理结束时引发一个Event并响应该事件构造读取模型。

  4. 如果由于系统错误(磁盘已满,死锁等)而导致查询模型构建失败,则可以依靠消息队列机制来跟踪和处理异常和失败的事务。 (如果您尚未实现重试/处理消息处理失败的机制,则最好添加它。)

此机制假定您没有打包响应于应用程序层中命令的查询模型。如果这样做,则应避免这样做。假设并执行命令,好像命令在处理后永不返回任何数据。