有关域对象的存储库及其Save方法的问题

时间:2009-03-23 10:18:31

标签: nhibernate linq-to-sql orm domain-driven-design ddd-repositories

关于DDD,Repository Patterns和ORM,我有一个有点荒谬的问题。在此示例中,我有3个类:地址公司人员。人是公司的成员并拥有地址。公司也有一个地址。

这些类反映了数据库模型。我删除了我的模型的任何依赖项,因此它们不依赖于特定的ORM库,如NHibernate或LinqToSql。这些依赖关系在存储库中处理。

在其中一个存储库中有一个 SavePerson(人员)方法插入/更新一个人,具体取决于它是否已存在于数据库。

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

但是,我真的很难想到公司的数据在与人交往时可能会发生变化的情况 - 我只希望能够将公司分配给一个人,或者将一个人转移到另一个公司。我认为我不想和新人一起创建新公司。因此SaveCompany调用会引入不必要的数据库调用。保存人员时,我应该能够更新CompanyId列。

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

首选方式是什么?只需在保存人员或保存其所有数据时插入/更新公司财产的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; }

}

修改

另见this follow up question。值对象如何存储在数据库中?

2 个答案:

答案 0 :(得分:6)

我自己最近使用了IRepository方法,Keith建议。但是,你不应该在这里专注于这种模式。相反,DDD剧本中还有一些可以在这里应用的部分。

使用值对象作为地址

首先,您可以在此处应用值对象(VO)的概念。在你的情况下,它将是地址。值对象和实体对象之间的区别在于实体具有标识; VO没有。 VO的身份确实是它的属性的总和,而不是唯一的身份。在书Domain-Drive Design Quickly(它也是一个免费的PDF下载)中,他通过声明地址实际上只是地球上的一个点并且不需要像人一样的单独的类似社会安全的身份来解释这一点。地球上的这一点是街道,数字,城市,拉链和国家的组合。它可以有纬度和经度值,但根据定义它们甚至是VO,因为它是两点的组合。

使用服务将您的实体合并到一个实体中进行操作。

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

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

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

ORMs usualyl需要一个身份,期限。

现在,您将如何在数据库中保存地址VO?你会明显使用IAddressRepository。但是由于大多数ORM(即LingToSql)要求所有对象都具有Identity,这里的诀窍是:将标识标记为模型中的内部,因此它不会暴露在模型层之外。这是Steven Sanderson自己的建议。

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; }
  ...
}

答案 1 :(得分:0)

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

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

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

我觉得关于您的描述中的行为的问题将是域的关注,也许您应该在Person类中使用AddCompany方法并将Company属性更改为

public Company Company { get; private set; }

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

回到存储库,看看这个post,了解LinqToSql上的IRepository。 Mike的博客在Repositories上有很多其他帖子。当你选择ORM我可以通过LinqToSql推荐HHibernate,后者现在已经不存在了,NHibernate有一个很好的支持社区。