Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Domain driven design 用AutoFac装饰存储库_Domain Driven Design_Repository Pattern_Decorator_Autofac - Fatal编程技术网

Domain driven design 用AutoFac装饰存储库

Domain driven design 用AutoFac装饰存储库,domain-driven-design,repository-pattern,decorator,autofac,Domain Driven Design,Repository Pattern,Decorator,Autofac,您好,我有一个可能是一个常见的问题,我认为Autofac或任何IoC容器都不能完全解决这个问题。这可能是一个设计问题,我需要一些新的投入 我有EF6的经典MVC web解决方案。它是以真正的DDD风格实施的,具有反腐败层、三个有界上下文、跨领域关注点和基础设施项目。看到所有部件都以良好的方式安装到位,我感到非常高兴。我们还向域中添加了CUD操作的命令 现在问题来了。客户需要一个跟踪每个实体属性的更改日志,更新完成后,我们需要在更新前后将其保存到更改日志值中。我们已经在一个iLogger服务中成功

您好,我有一个可能是一个常见的问题,我认为Autofac或任何IoC容器都不能完全解决这个问题。这可能是一个设计问题,我需要一些新的投入

我有EF6的经典MVC web解决方案。它是以真正的DDD风格实施的,具有反腐败层、三个有界上下文、跨领域关注点和基础设施项目。看到所有部件都以良好的方式安装到位,我感到非常高兴。我们还向域中添加了CUD操作的命令

现在问题来了。客户需要一个跟踪每个实体属性的更改日志,更新完成后,我们需要在更新前后将其保存到更改日志值中。我们已经在一个iLogger服务中成功地实现了这一点,该服务包装了一个Microsoft测试实用程序,我们使用它来检测更改。但是我,我的角色是软件架构师,决定用一个依赖于
ilogger服务的
ChangeTrackerRepository
来装饰我们的通用存储库。这个很好用。装饰器在我们的
IRepository
中跟踪方法
Add(…)
Modify(…)

问题是,我们的存储库具有自定义存储库,这些存储库具有如下自定义查询:

public class CounterPartRepository : Repository<CounterPart>, ICounterPartRepository
{
    public CounterPartRepository(ManagementDbContext unitOfWork)
        : base(unitOfWork)
    {}

    public CounterPart GetAggregate(Guid id)
    {
        return GetSet().CompleteAggregate().SingleOrDefault(s => s.Id == id);
    }

    public void DeleteCounterPartAddress(CounterPartAddress address)
    {
       RemoveChild(address);
    }

    public void DeleteCounterPartContact(CounterPartContact contact)
    {
        RemoveChild(contact);
    }


}
public类CounterPartRepository:Repository,ICounterPartRepository
{
公共交易对手存储库(ManagementDbContext unitOfWork)
:基础(工作单元)
{}
公共对应项GetAggregate(Guid id)
{
返回GetSet().CompleteAggregate().SingleOrDefault(s=>s.Id==Id);
}
公共无效删除对方地址(对方地址)
{
RemoveChild(地址);
}
公共无效删除对方联系人(对方联系人)
{
RemoveChild(联系人);
}
}
我们有一些简单的存储库,只需关闭通用存储库并将适当的EF绑定上下文注入其中(工作单元模式):

公共类AccountalPeriodTypeRepository:Repository)


我想,当你有了自定义的封闭库和简单的存储库,但都继承了同一个存储库

时,你需要考虑一些新的输入来解决一个共同的问题吗?你是否考虑过装饰命令处理程序而不是装饰存储库? 回购协议的水平太低,他们没有责任知道应该记录什么以及如何记录

那么以下内容呢:

1) 您的命令处理程序的方式如下:

public class DeleteCounterPartAddressHandler  : IHandle<DeleteCounterPartAddressCommand> 
{
    //this might be set by a DI container, or passed to a constructor
    public ICounterPartRepository Repository { get; set; }    

    public void Handle(DeleteCounterPartAddressCommand command)
    {
         var counterpart = repository.GetPropertyById(command.CounterPartId);

         // in DDD you always want to read and aggregate 
         // and save an aggregate as a whole
         property.DeleteAdress(command.AddressId);

         repository.Save(counterpart)
    }
}
public类DeleteCounterPartAddressHandler:IHandle
{
//这可以由DI容器设置,也可以传递给构造函数
公共ICounterPartRepository存储库{get;set;}
公共无效句柄(DeleteCounterPartAddressCommand命令)
{
var对应方=repository.GetPropertyById(command.CounterPartId);
//在DDD中,您总是希望读取和聚合
//并将聚合作为一个整体保存
property.deleteAddress(command.AddressId);
repository.Save(副本)
}
}
2) 现在,您可以简单地使用责任链模式,用日志记录、事务等“装饰”处理程序:

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

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

    public void Handle(T command) 
    {
        //Obviously you do it properly, but you get the idea
        _log.Info("Before");
        _innerHandler.Handle(command);
        _log.Info("After");
    }
}
公共类日志处理程序:IHandler{
私有只读IHandler\u innerHandler;
公共日志处理程序(IHandler innerHandler){
_innerHandler=innerHandler;
}
公共无效句柄(T命令)
{
//显然你做得很好,但你明白了
_日志信息(“之前”);
_Handle(命令);
_日志信息(“之后”);
}
}
现在,您只有一段负责记录的代码,您可以将其与任何命令处理程序组合,因此,如果您想要记录特定命令,那么您只需使用日志处理程序“包装”它,它仍然是您的
IHandle
,这样系统的其余部分就不会受到影响。 您还可以处理其他问题(线程、排队、事务、多路复用、路由等),而不必到处乱搞和布置这些东西。 关注点以这种方式很好地分开

它(对我来说)也更好,因为您登录的是真实的操作(业务)级别,而不是低级存储库

希望能有帮助

p.S.在DDD中,您确实希望您的存储库只公开聚合级方法,因为聚合假定要处理它们的不变量(没有其他,没有服务,没有存储库),并且因为聚合表示事务边界

实际上,这取决于存储库如何从持久化存储中获取聚合以及如何将其持久化,在外部,它应该看起来像是您向某人请求对象,而它会给您一个可以调用的对象


因此,通常您只能从存储库中获取聚合,调用其行为,然后将其保存回去。这实际上意味着您的存储库大部分都有GetById和Save方法,而不是像“updatehattpartofaggregate”这样的内部方法。

我们在Commandlevel上有日志记录。我们所有的命令都被序列化为Json并记录下来。问题是该客户有记录更改的特定业务需求。例如,CounterpartAddressChanged:Street Before-->After,Zipcode Before-->After。但不是城镇,因为它并没有改变。我完全同意你们关于总边界和积垢应该如何处理的观点。我们的一些CodeFirst映射关系导致EF不删除父子关系(聚合内)中的子实体。所以RemoveChild是一个解决方法。但是我们有其他定制(大部分是读取)方法,比如getcounterpartbyconstruct(…)这样的方法必须存在,因为您无法通过唯一ID获取所有信息……对于倾向于查询但在聚合边界内的存储库方法,您有什么经验?另一种方法不是将更改表示为状态,而是表示为事件流(EventSourc)
public class DeleteCounterPartAddressHandler  : IHandle<DeleteCounterPartAddressCommand> 
{
    //this might be set by a DI container, or passed to a constructor
    public ICounterPartRepository Repository { get; set; }    

    public void Handle(DeleteCounterPartAddressCommand command)
    {
         var counterpart = repository.GetPropertyById(command.CounterPartId);

         // in DDD you always want to read and aggregate 
         // and save an aggregate as a whole
         property.DeleteAdress(command.AddressId);

         repository.Save(counterpart)
    }
}
public class LoggingHandler<T> : IHandler<T>  {
    private readonly IHandler<T> _innerHandler;

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

    public void Handle(T command) 
    {
        //Obviously you do it properly, but you get the idea
        _log.Info("Before");
        _innerHandler.Handle(command);
        _log.Info("After");
    }
}