Language agnostic 数据存储库组织

Language agnostic 数据存储库组织,language-agnostic,repository-pattern,Language Agnostic,Repository Pattern,所以,我正在开发一些软件,并试图让自己使用TDD和其他最佳实践 我试图编写测试来定义类和存储库 假设我有类,Customer,Order,OrderLine 现在,我是否创建Order类,如下所示 abstract class Entity { int ID { get; set; } } class Order : Entity { Customer Customer { get; set; } List<OrderLine> OrderLines { g

所以,我正在开发一些软件,并试图让自己使用TDD和其他最佳实践

我试图编写测试来定义类和存储库

假设我有类,
Customer
Order
OrderLine

现在,我是否创建
Order
类,如下所示

abstract class Entity {
    int ID { get; set; }
}

class Order : Entity {
    Customer Customer { get; set; }
    List<OrderLine> OrderLines { get; set; }
}
您将如何为类似的内容构建存储库

public abstract class RepositoryBase<T, TID> : IRepository<T, TID> where T : AEntity<TID>
{
    private static List<T> Entities { get; set; }

    public RepositoryBase()
    {
        Entities = new List<T>();
    }

    public T GetByID(TID id)
    {
        return Entities.Where(x => x.Id.Equals(id)).SingleOrDefault();
    }

    public T Save(T entity)
    {
        Entities.RemoveAll(x => x.Id.Equals(entity.Id));
        Entities.Add(entity);
        return entity;
    }

    public T Delete(T entity)
    {
        Entities.RemoveAll(x => x.Id.Equals(entity.Id));
        return entity;
    }
}
我是否将抽象CRUD存储库与每个项目存储库继承的方法
GetByID(int)
Save(entity)
Delete(entity)
一起使用,并添加它自己的特定方法,类似这样的事情

public abstract class RepositoryBase<T, TID> : IRepository<T, TID> where T : AEntity<TID>
{
    private static List<T> Entities { get; set; }

    public RepositoryBase()
    {
        Entities = new List<T>();
    }

    public T GetByID(TID id)
    {
        return Entities.Where(x => x.Id.Equals(id)).SingleOrDefault();
    }

    public T Save(T entity)
    {
        Entities.RemoveAll(x => x.Id.Equals(entity.Id));
        Entities.Add(entity);
        return entity;
    }

    public T Delete(T entity)
    {
        Entities.RemoveAll(x => x.Id.Equals(entity.Id));
        return entity;
    }
}
公共抽象类RepositoryBase:IRepository其中T:AEntity
{
私有静态列表实体{get;set;}
公共存储库()
{
实体=新列表();
}
公共T GetByID(TID id)
{
返回实体。其中(x=>x.Id.Equals(Id)).SingleOrDefault();
}
公共T保存(T实体)
{
RemoveAll(x=>x.Id.Equals(entity.Id));
实体。添加(实体);
返回实体;
}
公共T删除(T实体)
{
RemoveAll(x=>x.Id.Equals(entity.Id));
返回实体;
}
}
这里的“最佳实践”是什么?

让我们从
Order
实体开始。订单是一个自治对象,它不依赖于“父”对象。在域驱动设计中,这称为聚合根;它是整个订单聚合的根。订单聚合由根实体和几个子实体组成,在本例中,它们是
OrderLine
实体

聚合根负责管理整个聚合,包括子实体的生存期。其他组件不允许访问子实体;对聚合的所有更改都必须经过根目录。此外,如果根不存在,子订单也不存在,即没有父订单,订单行就不能存在

Customer
也是一个聚合根。它不是订单的一部分,只是与订单相关。如果订单不存在,客户就不存在。另一方面,如果客户不复存在,您将希望保留订单用于记账目的。因为
Customer
只是相关的,所以您只希望订单中有
CustomerId

class Order
{
  int OrderId { get; }

  int CustomerId { get; set; }

  IEnumerable<OrderLine> OrderLines { get; private set; }
}
class OrderRepository
{
  Order GetById(int orderId)
  {
    // implementation details
  }

  Order GetById(int orderId, OrderLoadOptions loadOptions)
  {
    // implementation details
  }
}

enum OrderLoadOptions
{
  All,
  ExcludeOrderLines,
  // other options
}
如果以后需要加载订单行,则应使用“告诉,不要问”原则。告诉订单加载其订单行,以及使用哪个存储库。然后,订单将告诉存储库它需要知道的信息

class Order
{
  int OrderId { get; }

  int CustomerId { get; set; }

  IEnumerable<OrderLine> OrderLines { get; private set; }

  void LoadOrderLines(IOrderRepository orderRepository)
  {
    // simplified implementation
    this.OrderLines = orderRepository.GetOrderLines(this.OrderId);
  }
}
类顺序
{
int OrderId{get;}
int CustomerId{get;set;}
IEnumerable命令行{get;private set;}
void LoadOrderLines(IORDRepository orderRepository)
{
//简化实施
this.OrderLines=orderRepository.GetOrderLines(this.OrderId);
}
}
请注意,代码使用
iordrepository
检索订单行,而不是单独的订单行存储库。域驱动设计声明每个聚合根都应该有一个存储库。用于检索子实体的方法属于根目录的存储库,并且只能由根目录访问

抽象/基本存储库 我自己编写了带有CRUD操作的抽象存储库,但我发现它没有增加任何价值。当您希望在代码中传递子类的实例时,抽象非常有用。但是什么样的代码会接受任何
BaseRepository
实现作为参数呢

此外,CRUD操作可能因实体而异,这使得基本实现毫无用处。是否确实要删除订单,或只是将其状态设置为“已删除”?如果删除客户,相关订单会发生什么情况


我的建议是保持简单。远离抽象和泛型基类。当然,所有存储库都共享某种功能,并且泛型看起来很酷。但是您真的需要它吗?

为什么不创建单独的订单类呢?在我看来,您描述的是一个基本订单对象,其中包含基本订单和客户信息(甚至可能不包含客户信息),以及一个单独的订单对象,其中包含行项目

在过去,我按照Niels的建议做了,或者使用布尔标志或枚举来描述选择性地加载子对象、列表等。在中,Bob叔叔说,这些变量和函数参数是程序员不将类或函数重构为更小、更容易理解的部分的借口


至于你的课堂设计,我认为这要看情况而定。我假设一个订单可以没有任何订单行存在,但是如果没有客户就不可能存在(或者至少有一种参考客户的方式,就像尼尔斯建议的那样)。如果是这种情况,为什么不创建一个基本订单类和第二个FullOrder类呢。只有FullOrder将包含订单行列表。按照这个想法,我会创建单独的存储库来处理Order和FullOrder的CRUD操作。

我会将我的项目划分为相关部分。数据传输对象(DTO)、数据访问对象(DAO)。DTO我希望尽可能简单,这里使用POJO(普通旧Java对象)和POCO(普通旧C对象)等术语,简单地说,它们是容器对象,内置的功能非常少

DTO基本上是整个应用程序的构建块,并将各层结合起来。对于系统中建模的每个对象,至少应有一个DTO。然后如何将它们放入集合完全取决于应用程序的设计。显然,存在着自然的一对多关系,比如客户有很多订单。但这些物体的基本原理是它们是什么。例如
    class OrderDAO
    {
    public OrderDTO Create(IOrderDTO order)
    {
    //Code here that will create the actual order and store it, updating the 
flelds in the OrderDTO where necessary. One being the GUID field of the new ID. 
I stress guid as this means for better scalability.

    return OrderDTO
    }

}