9

关于 DDD、存储库模式和 ORM,我有一个有点荒谬的问题。在这个例子中,我有 3 个类:AddressCompanyPerson。一个人是公司的成员并且有一个地址。公司也有地址。

这些类反映了一个数据库模型。我删除了我的模型的所有依赖项,因此它们不会绑定到特定的 ORM 库,例如 NHibernate 或 LinqToSql。这些依赖关系在存储库中处理。

在其中一个存储库中有一个SavePerson(Person person)方法,该方法根据 Person 是否已存在于数据库中来插入/更新它。

由于 Person 对象有一个 Company,因此我目前在进行 SavePerson 调用时也保存/更新了 Company 属性的值。在此过程中,我插入/更新公司的所有数据 - 名称和地址。

但是,我真的很难想到公司的数据在与人打交道时会发生变化的情况——我只想能够将公司分配给一个人,或者将一个人移动到另一个公司。我不认为我想和一个新人一起创建一个新公司。所以 SaveCompany 调用引入了不必要的数据库调用。保存 Person 时,我应该能够更新 CompanyId 列。

但是由于 Person 类有一个 Company 属性,我有点倾向于用它来更新/插入它。从严格/纯粹的角度来看,SavePerson 方法应该保存整个 Person。

首选的方式是什么?只是在保存 Person 或保存其所有数据时插入/更新 Company 属性的 CompanyId?或者你会为这两种场景创建两种不同的方法(你会给它们起什么名字?)

另外,另一个问题,我目前有不同的方法来保存一个人、一个地址和一个公司,所以当我保存一个公司时,我也会调用 SaveAddress。假设我使用 LinqToSql——这意味着我不在同一个 Linq 查询中插入/更新公司和地址。我猜有 2 个 Select Calls(检查公司是否存在,检查地址是否存在)。然后对两者进行两次插入/更新调用。如果引入更多的复合模型类,则更多。LinqToSql 有没有办法优化这些调用?

public class Address
{
    public int AddressId { get; set; }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }        
}

public class Company
{
    public int CompanyId { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}


public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public Company Company { get; set; }
    public Address Address { get; set; }

}

编辑

另请参阅此后续问题。值对象如何存储在数据库中?

4

2 回答 2

6

我自己最近使用了 Keith 建议的 IRepository 方法。但是,您不应该在这里关注这种模式。取而代之的是,DDD 手册中还有一些可以在此处应用的部分。

为您的地址使用值对象

首先,您可以在此处应用值对象 (VO) 的概念。在您的情况下,它将是地址。值对象和实体对象的区别在于实体有一个身份;VO没有。VO 的身份实际上是其属性的总和,而不是唯一身份。在Domain-Drive Design Quickly(它也是一个免费的 PDF 下载)一书中,他很好地解释了这一点,他指出地址实际上只是地球上的一个点,不需要像人这样的单独的类似 SocialSecurity 的身份。地球上的那个点是街道、号码、城市、邮编和国家的组合。它可以具有纬度和经度值,但根据定义,它们仍然是 VO,因为它是两点的组合。

使用服务将您的实体组合成一个实体以进行操作。

另外,不要忘记 DDD 手册中的服务概念。在您的示例中,该服务将是:

public class PersonCompanyService
{
  void SavePersonCompany(IPersonCompany personCompany)
  {
    personRepository.SavePerson();
    // do some work for a new company, etc.
    companyRepository.SaveCompany();
  }
}

当您有两个需要类似操作来协调其他操作组合的实体时,就需要服务。在您的情况下,同时保存 Person() 并创建一个空白 Company() 。

ORM 通常需要一个身份,句号。

现在,您将如何将地址 VO 保存在数据库中?您显然会使用 IAddressRepository。但是由于大多数 ORM(即 LingToSql)要求所有对象都有一个身份,所以这里的诀窍是:在你的模型中将身份标记为内部,因此它不会暴露在你的模型层之外。这是史蒂文桑德森自己的建议。

public class Address
{
  // make your identity internal
  [Column(IsPrimaryKey = true
    , IsDbGenerated = true
    , AutoSync = AutoSync.OnInsert)]
  internal int AddressID { get; set; }

  // everything else public
  [Column]
  public string StreetNumber { get; set; }
  [Column]
  public string Street { get; set; }
  [Column]
  public string City { get; set; }
  ...
}
于 2009-03-23T22:03:56.353 回答
0

根据我最近使用存储库模式的经验,我认为您会从使用通用存储库(现在常见的 T 的 IRepository)中受益。这样您就不必添加像 SavePerson(Person person) 这样的存储库方法。相反,你会有类似的东西:

IRepository<Person> personRepository = new Repository<Person>();
Person realPerson = new Person();
personRepository.SaveOrUpdate(realPerson);

这种方法也很适合测试驱动开发和模拟。

我觉得您描述中有关行为的问题与域有关,也许您应该在您的 Person 类中有一个 AddCompany 方法并将 Company 属性更改为

public Company Company { get; private set; }

我的观点是;对域进行建模,而不必担心数据将如何持久化到数据库中。这是将使用您的域模型的服务的一个问题。

回到存储库,看看这篇文章,了解 IRepository over LinqToSql 的良好解释。Mike 的博客上有许多其他关于 Repositories 的帖子。当你选择 ORM 时,我可以推荐 HHibernate 而不是 LinqToSql,后者现在已经不复存在,NHibernate 有一个很好的支持社区。

于 2009-03-23T16:23:15.020 回答