在Domain Layer中公开IUnitOfWork接口会违反Persistence Ignorance规则吗?

时间:2013-12-16 17:22:28

标签: domain-driven-design unit-of-work persistence-ignorance

1)在大多数情况下,每个Aggregate Root应定义自己的事务边界,在这种情况下,我们不需要在IUnitOfWork中公开Domain Layer接口。

a)我假设在这种情况下,一个很好的选择是repository(由aggregate用来强制invariants在其中应用)来包含它自己的{{1}实例1}}(如果使用EF,那么这个UoW实例可能只是类型UoW)?

2)

a)但是如果由于某种原因DbContext跨越多个transaction(因此需要一次更改多个aggregates),那么aggregate将不会Domain Layer还需要包含IUnitOfWork接口吗?

b)不会在IUnitOfWork中暴露Domain Layer界面违反持久性无知规则吗?

c)如果 b)是,则不会因IUnitOfWork而忽略repositories的目的吗?

回复Alexey Raga:

1) I would advice against exposing repositories to aggregates. Repositories are there to give you aggregates, that's it.

a)虽然我认为大多数ddd架构师都没有将回购暴露给聚合的问题(我只是问,因为我读了几篇关于回购和DDD的文章,我得到的印象是作者不是反对暴露回购聚合 - 但现在我不再那么肯定了)?

b)所以你也反对将存储库暴露给域服务?

c)根据你的回答判断你是否认为你认为IUnitOfWork是违反PI的?

2)Note that although my command handler (app service in a way)...

您通常将命令处理程序实现为app服务吗?

3)

public void Handle(ApproveOrderCommand command)
{
    var order = Repository.Get(command.OrderId);
    property.Approve(command.Comment, ServiceRequiredForOrderApproval);
    Repository.Save(order);
}

property.Approve(...)是一个拼写错误,你的意思是order.Approve(...)

提前完成

3 个答案:

答案 0 :(得分:2)

我建议不要将存储库暴露给聚合。存储库可以为您提供聚合,就是这样。

以这种方式看待它:你的域名是一个“泡沫”,它只能理解自己的东西。意思是,它只了解它自己的值对象,它声明的域服务接口等等。我不会在这个集合中包含存储库。

当您的域(聚合)需要某些东西时,它应该明确地公开它所需要的依赖性,而不仅仅是要求一些存储库。

服务是将事物融合在一起的东西。 例如,我的命令处理程序可能如下所示:

public class ApproveOrderCommandHandler  : IHandle<ApproveOrderCommand> 
{
    //this might be set by a DI container, or passed to a constructor
    public IOrderRepository Repository { get; set; }
    public ISomeFancyDomainService ServiceRequiredForOrderApproval { get; set; }  

    public void Handle(ApproveOrderCommand command)
    {
        var order = Repository.Get(command.OrderId);
        order.Approve(command.Comment, ServiceRequiredForOrderApproval);
        Repository.Save(order);
    }
}

请注意,虽然我的命令处理程序(某种方式中的应用程序服务)处理存储库,但我的域(订单聚合)是持久性无知的。它对UnitOfWorks的存储库一无所知。

当我需要启动UnitOfWork时,我可以使用Chain of Responsibility模式撰写

public class WithUnitOfWorkHandler<T> : IHandler<T>  {
    private readonly IHandler<T> _innerHandler;

    public WithUnitOfWorkHandler(IHandler<T> innerHandler)  {
        _innerHandler = innerHandler;
    }

    public void Handle(T command) {
        using(var ouw = new UnitOfWork()) {
            _innerHandler.Handle(command);
            uow.Commit();
        }
    }
}

现在,我可以通过WithUnitOfWorkHandler“装饰”它来“链接”我的命令处理程序的任何。 并且一些处理程序甚至可能触及多个存储库或聚合。但是,聚合对持久性,工作单元,交易等一无所知。

答案 1 :(得分:2)

持久性无知意味着:业务层对于在底层使用的具体持久性系统(例如MS SQL Server,Oracle,XML文件等)没有任何知识和依赖性。

因此,暴露一个抽象出数据存储的具体类型的接口永远不会违反这个原则。

答案 2 :(得分:1)

持久性无知是一个指导原则,几乎不可能用实际的语言和技术来实现。存储库模式和工作单元抽象了持久性相关的东西,并且&#34;隐藏&#34;数据访问层到业务代码,但它绝对是绝对解决方案(一个干净的技巧)。某事物的存在或需要(界面,基类,属性......)说&#34; heyyy,这里有一些我们想隐藏的东西......&#34;违反PI。但目前还没有更好的解决方案。