2

我有一个独特的情况,我正在构建一个基于 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() 方法中,但现在它没有家了。工作单元只知道实体框架数据上下文。这个逻辑应该去哪里?我认为这种设计只有在您只有一个使用实体框架的数据存储时才有效。任何想法如何最好地解决这个问题?我应该把这个逻辑放在哪里?

4

3 回答 3

4

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

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

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

于 2011-02-08T22:59:51.963 回答
1

首先,我假设您使用的是 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 之间的原子性,这是一个单独的重要问题。

于 2011-01-20T18:47:44.313 回答
0

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

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

于 2010-05-29T08:49:49.297 回答