首页 > 解决方案 > 如何在相同的有界上下文中将查询数据库与命令数据库同步?

问题描述

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

我将 NH 用于写入(命令)端,将 EF 用于读取(查询)端。我为 NH 使用了 Unit Of Work 并使用 Unit Of Work 修饰了我的所有命令,并使它们在每个聚合中都具有事务性:

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

}

但我不知道是什么问题。一切都运行没有一个错误,但我已读取保存到查询数据库中的模型并写入模型剂量。

如果我应该为读取模型设置单独的事务装饰器,我如何确保正确运行两个事务而不会出错?或者如果发现错误则回滚?

或者命令完全错误,我不知道如何处理和同步读写!

标签: c#domain-driven-designcqrs

解决方案


我对 NH、EF 和事务管理知之甚少,所以这将是一个部分答案,试图解决您关于有单独的写入和读取事务的问题。

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

    一个。查询模型往往很庞大

    湾。根据需求构建的查询模型可能不止一个,通常由不同的 UI 屏幕或视图模型决定

  2. 其次,为确保事务在查询模型持久性期间不会失败,您在命令模型事务中预先进行所有验证。查询模型构造应该是一种基本的收集和序列化类型的活动,它以您想要的格式生成文档。它不应该涉及验证。

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

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

此机制假定您没有打包查询模型以响应应用层中的命令。如果你这样做,你应该避免它。假设并表现得好像命令在处理后从不返回任何数据。


推荐阅读