Domain driven design 我们都在寻找相同的假设吗?

Domain driven design 我们都在寻找相同的假设吗?,domain-driven-design,design-patterns,irepository,Domain Driven Design,Design Patterns,Irepository,我一直在试图找到一种编写通用存储库的方法,该存储库可用于各种数据存储: public interface IRepository { IQueryable<T> GetAll<T>(); void Save<T>(T item); void Delete<T>(T item); } public class MemoryRepository : IRepository {...} public class SqlReposi

我一直在试图找到一种编写通用存储库的方法,该存储库可用于各种数据存储:

public interface IRepository
{
    IQueryable<T> GetAll<T>();
    void Save<T>(T item);
    void Delete<T>(T item);
}
public class MemoryRepository : IRepository {...}
public class SqlRepository : IRepository {...}
公共接口IRepository
{
IQueryable GetAll();
作废保存(T项);
作废删除(T项);
}
公共类MemoryRepository:IRepository{…}
公共类SqlRepository:IRepository{…}
我希望在每个域中使用相同的POCO域类。我还考虑采用类似的方法,每个域类都有自己的存储库:

public interface IRepository<T>
{
    IQueryable<T> GetAll();
    void Save(T item);
    void Delete(T item);
}
public class MemoryCustomerRepository : IRepository {...}
public class SqlCustomerRepository : IRepository {...}
公共接口IRepository
{
IQueryable GetAll();
作废保存(T项);
作废删除(T项);
}
公共类MemoryCustomerRepository:IRepository{…}
公共类SqlCustomerRepository:IRepository{…}
我的问题是:1)第一种方法是否可行?2) 第二种方法有什么好处吗

  • 第一种方法是可行的,我在过去编写自己的映射框架时也做过类似的事情,该框架针对RDBMS和
    XmlWriter
    /
    XmlReader
    。您可以使用这种方法来简化单元测试,尽管我认为现在我们有更好的OSS工具来实现这一点

  • 第二种方法是我目前使用的方法。每个映射器都有一个接口,每个映射器都可以提供基本的CRUD操作。优点是域类的每个映射器还具有由接口表示并在具体映射器中定义的特定功能(例如
    SelectByLastName
    DeleteFromParent
    )。正因为如此,我没有必要像您所建议的那样实现单独的存储库——我们的具体映射器以数据库为目标。为了执行单元测试,我使用它并创建内存中的存储库,这些存储库可以像
    memory*存储库那样运行。对于一个非常可测试的方法来说,它的实现和管理类更少,总体工作也更少。对于跨单元测试共享的数据,我为每个域类使用一个生成器模式,该域类具有
    WithXXX
    方法和
    AsSomeProfile
    方法(AsSomeProfile
    仅返回一个带有预配置测试数据的生成器实例)

  • 下面是一个我通常在单元测试中得到的示例:

    // Moq mocking the concrete PersonMapper through the IPersonMapper interface
    var personMock = new Mock<IPersonMapper>(MockBehavior.Strict);
    personMock.Expect(pm => pm.Select(It.IsAny<int>())).Returns(
        new PersonBuilder().AsMike().Build()
    );
    
    // StructureMap's ObjectFactory
    ObjectFactory.Inject(personMock.Object);
    
    // now anywhere in my actual code where an IPersonMapper instance is requested from
    // ObjectFactory, Moq will satisfy the requirement and return a Person instance
    // set with the PersonBuilder's Mike profile unit test data
    
    //Moq通过ipersonapper接口模拟具体的personapper
    var personMock=newmock(MockBehavior.Strict);
    Expect(pm=>pm.Select(It.IsAny())。返回(
    新建PersonBuilder().AsMike().Build()
    );
    //StructureMap的ObjectFactory
    ObjectFactory.Inject(personMock.Object);
    //现在,在我的实际代码中,任何一个请求IPersonMapper实例的地方
    //ObjectFactory、Moq将满足要求并返回一个Person实例
    //使用PersonBuilder的Mike profile单元测试数据进行设置
    
    事实上,现在人们普遍认为域存储库不应该是通用的。存储库应该说明在持久化或检索实体时可以执行的操作

    有些存储库是只读的,有些是只插入的(没有更新,没有删除),有些只有特定的查找

    使用GetAll返回IQueryable,查询逻辑将泄漏到代码中,可能泄漏到应用程序层

    但使用您提供的接口封装Linq
    对象仍然很有趣,这样您就可以用内存中的实现来代替它进行测试

    因此,我建议称之为
    ITable
    ,为它提供与linq
    对象相同的接口,并在特定域存储库中使用它(而不是替代)

    然后,您可以通过使用内存
    ITable
    实现在内存中使用特定的存储库

    * Supports all CRUD methods ( Create, Retrieve, Update, Delete )
    * Supports aggregate methods Min, Max, Sum, Avg, Count
    * Supports Find methods using ICriteria<T>
    * Supports Distinct, and GroupBy
    * Supports interface IRepository<T> so you can use an In-Memory table for unit-testing
    * Supports versioning of your entities
    * Supports paging, eg. Get(page, pageSize)
    * Supports audit fields ( CreateUser, CreatedDate, UpdateDate etc )
    * Supports the use of Mapper<T> so you can map any table record to some entity
    * Supports creating entities only if it isn't there already, by checking for field values.
    
    在内存中实现
    ITable
    的最简单方法是使用
    List
    并使用.AsQueryable()扩展方法获得
    IQueryable
    接口

    public class InMemoryTable<T> : ITable<T>
    {
        private List<T> list;
        private IQueryable<T> queryable;
    
       public InMemoryTable<T>(List<T> list)
       { 
          this.list = list;
          this.queryable = list.AsQueryable();
       }
    
       public void Add(T entity) { list.Add(entity); }
       public void Remove(T entity) { list.Remove(entity); }
    
       public IEnumerator<T> GetEnumerator() { return list.GetEnumerator(); }
    
       public Type ElementType { get { return queryable.ElementType; } }
       public IQueryProvider Provider {     get { return queryable.Provider; } }
       ...
    }
    
    InMemoryTable中的公共类:ITable
    {
    私人名单;
    私有的可查询的;
    公共InMemoryTable(列表)
    { 
    this.list=列表;
    this.queryable=list.AsQueryable();
    }
    公共void Add(T实体){list.Add(实体);}
    公共无效删除(T实体){list.Remove(实体);}
    公共IEnumerator GetEnumerator(){返回列表。GetEnumerator();}
    公共类型ElementType{get{return queryable.ElementType;}}
    公共IQueryProvider提供程序{get{return queryable.Provider;}}
    ...
    }
    

    您可以独立于数据库进行测试,但是使用真正的特定存储库,可以提供更多的领域洞察力。

    这有点晚了。。。但请看一下codeplex上的IRepository实现。它有一个非常好的功能集

    关于您的问题,我看到很多人使用诸如GetAllProducts()、GetAllEmployees()之类的方法 在他们的存储库实现中。这是多余的,不允许您的存储库是通用的。 您只需要GetAll()或All()。不过,上面提供的解决方案确实解决了命名问题

    这是从CommonLibrary.NET联机文档中获取的:

    0.9.4 Beta 2有一个强大的存储库实现

    * Supports all CRUD methods ( Create, Retrieve, Update, Delete )
    * Supports aggregate methods Min, Max, Sum, Avg, Count
    * Supports Find methods using ICriteria<T>
    * Supports Distinct, and GroupBy
    * Supports interface IRepository<T> so you can use an In-Memory table for unit-testing
    * Supports versioning of your entities
    * Supports paging, eg. Get(page, pageSize)
    * Supports audit fields ( CreateUser, CreatedDate, UpdateDate etc )
    * Supports the use of Mapper<T> so you can map any table record to some entity
    * Supports creating entities only if it isn't there already, by checking for field values.
    
    *支持所有CRUD方法(创建、检索、更新、删除)
    *支持聚合方法Min、Max、Sum、Avg、Count
    *支持使用ICriteria查找方法
    *支持Distinct和GroupBy
    *支持接口IRepository,因此您可以使用内存中的表进行单元测试
    *支持实体的版本控制
    *支持分页,例如Get(page,pageSize)
    *支持审核字段(CreateUser、CreateDate、UpdateDate等)
    *支持使用映射器,因此您可以将任何表记录映射到某个实体
    *通过检查字段值,仅当实体不存在时才支持创建实体。