【问题标题】:How do I implement repository pattern and unit of work when dealing with multiple data stores?在处理多个数据存储时如何实现存储库模式和工作单元?
【发布时间】:2010-05-26 13:22:12
【问题描述】:

我有一个独特的情况,我正在构建一个基于 DDD 的系统,该系统需要同时访问 Active Directory 和 SQL 数据库作为持久性。最初这不是问题,因为我们的设计是在我们的工作单元中设置的,看起来像这样:

public interface IUnitOfWork
{
   void BeginTransaction()
   void Commit()
}

我们的存储库看起来像这样:

public interface IRepository<T>
{
   T GetByID()
   void Save(T entity)
   void Delete(T entity)
}

在此设置中,我们的加载和保存将处理两个数据存储之间的映射,因为我们自己编写了它。工作单元将处理事务并包含存储库将用于持久性的 Linq To SQL 数据上下文。活动目录部分由基础设施中实现的域服务处理,并由每个 Save() 方法中的存储库使用。 Save() 负责与数据上下文交互以执行所有数据库操作。

现在我们正在尝试使其适应实体框架并利用 POCO。理想情况下,我们不需要 Save() 方法,因为对象上下文正在跟踪域对象,我们只需要在工作单元上添加 Save() 方法以让对象上下文保存更改,以及一种方法用上下文注册新对象。新提议的设计看起来更像这样:

public interface IUnitOfWork
{
   void BeginTransaction()
   void Save()
   void Commit()
}

public interface IRepository<T>
{
   T GetByID()
   void Add(T entity)
   void Delete(T entity)
}

这解决了实体框架的数据访问问题,但没有解决我们的活动目录集成的问题。以前,它在存储库的 Save() 方法中,但现在它没有家了。工作单元只知道实体框架数据上下文。这个逻辑应该去哪里?我认为这种设计只有在您只有一个使用实体框架的数据存储时才有效。任何想法如何最好地解决这个问题?我应该把这个逻辑放在哪里?

【问题讨论】:

    标签: domain-driven-design repository-pattern unit-of-work repository ddd-repositories


    【解决方案1】:

    我想回来跟进我发布这篇文章后学到的东西。看来,如果您要忠实于存储库模式,那么它所坚持的数据存储就无关紧要了。如果有两个数据存储,则将它们都写入同一个存储库中。重要的是保持存储库模式所代表的外观:内存中的集合。我不会做单独的存储库,因为这对我来说不是一个真正的抽象。你让引擎盖下的技术在那个时候决定设计。引用 dddstepbystep.com:

    存储库背后是什么?漂亮的 很多你喜欢的东西。是的,你听到了 对的。你可以有一个数据库, 或者你可以有很多不同的 数据库。你可以使用关系 数据库或对象数据库。你 可能有一个内存数据库,或者 包含 in 列表的单例 记忆项目。你可以有一个 REST 层,或一组 SOA 服务,或一个 文件系统,或内存缓存…… 你几乎可以拥有任何东西—— 你唯一的限制是 存储库应该能够像 一个集合到您的域。这 灵活性是一个关键的区别 存储库和传统之间 数据访问技术。

    http://thinkddd.com/assets/2/Domain_Driven_Design_-_Step_by_Step.pdf

    【讨论】:

      【解决方案2】:

      首先我假设您使用的是 IoC 容器。我提倡您为每种实体类型创建真正的存储库。这意味着您将把每个对象上下文 EntitySet 包装在一个实现类似以下内容的类中:

      interface IRepository<TEntity> {
        TEntity Get(int id);
        void Add(TEntity entity);
        void Save(TEntity entity);
        void Remove(TEntity entity);
        bool CanPersist<T>(T entity);
      }
      

      CanPersist 仅返回该存储库实例是否支持持久化传递的实体,并由下面描述的 UnitOfWork.Save 多态使用。

      每个 IRepository 还将有一个构造函数,允许 IRepository 在“事务”模式下构造。所以,对于 EF,我们可能有:

      public partial EFEntityARepository : IRepository<EntityA> {
        public EFEntityARepository(EFContext context, bool transactional) {
          _context = context;
          _transactional = transactional;
        }
        public void Add(EntityA entity) {
          _context.EntityAs.Add(entity);
          if (!_transactional) _context.SaveChanges();
        }
      }
      

      UnitOfWork 应该如下所示:

      interface UnitOfWork {
        void Add(TEntity entity);
        void Save(TEntity entity);
        void Remove(TEntity entity);
        void Complete();
      }
      

      UnitOfWork 实现将使用依赖注入来获取所有 IRepository 的实例。在 UnitOfWork.Save/Add/Remove 中,UoW 将参数实体传递给每个 IRepository 的 CanPerist。对于任何true 返回值,UnitOfWork 都会将该实体存储在特定于该 IRepository 和预期操作的私有集合中。在 Complete 中,UnitOfWork 将遍历所有私有实体集合,并在每个实体的相应 IRepository 上调用相应的操作。

      如果您有一个需要由 EF 部分持久化并由 AD 部分持久化的实体,您将拥有该实体类型的两个 IRepository 类(当传递该实体类型的实例时,它们都会从 CanPersist 返回 true)。

      至于保持 EF 和 AD 之间的原子性,这是一个单独的重要问题。

      【讨论】:

        【解决方案3】:

        IMO 我会将对这两个存储库的调用包装在服务类型的类中。然后我会使用 IoC/DI 将 repo 类型注入到服务类中。您将有 2 个存储库,1 个用于 Ent。框架和 1 支持 AD。这样每个 repo 只处理其底层数据存储,而不必交叉。

        为了支持多种工作类型,我所做的就是让 IUnitOfWork 更像是一个工厂。我创建了另一种称为 IUnitOfWorkScope 的类型,它是实际的工作单元,它只有一个提交方法。

        namespace Framework.Persistance.UnitOfWork
        {
            public interface IUnitOfWork
            {
                IUnitOfWorkScope Get();
        
                IUnitOfWorkScope Get(bool shared);
            }
        
            public interface IUnitOfWorkScope : IDisposable
        {
            void Commit();
        }
        }
        

        这使我可以将工作单元的不同实现注入到服务中,并能够并排使用它们。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-09-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多