减少存储库以聚合根

e36*_*6M3 82 c# asp.net domain-driven-design entity-framework entity-framework-4

我目前拥有数据库中几乎每个表的存储库,并希望通过将它们简化为仅聚合根来进一步使自己与DDD保持一致.

我们假设我有以下表格,User并且Phone.每个用户可能有一部或多部电话.如果没有聚合根的概念,我可能会这样做:

//assuming I have the userId in session for example and I want to update a phone number
List<Phone> phones = PhoneRepository.GetPhoneNumberByUserId(userId);
phones[0].Number = “911”;
PhoneRepository.Update(phones[0]);
Run Code Online (Sandbox Code Playgroud)

聚合根的概念在纸上比在实践中更容易理解.我永远不会有不属于用户的电话号码,所以取消PhoneRepository并将电话相关的方法合并到UserRepository中是否有意义?假设答案是肯定的,我将重写先前的代码示例.

我是否允许在UserRepository上有一个返回电话号码的方法?或者它应该始终返回对用户的引用,然后通过用户遍历关系以获取电话号码:

List<Phone> phones = UserRepository.GetPhoneNumbers(userId);
// Or
User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone
Run Code Online (Sandbox Code Playgroud)

无论我采用哪种方式获取手机,假设我修改了其中一种,我该如何更新它们?我有限的理解是,根目录下的对象应该通过root更新,这将引导我选择下面的#1.尽管这对于Entity Framework非常有效,但这似乎非常缺乏描述性,因为读取代码时我不知道我实际更新了什么,即使实体框架在图形中保留了更改对象的选项卡.

UserRepository.Update(user);
// Or
UserRepository.UpdatePhone(phone);
Run Code Online (Sandbox Code Playgroud)

最后,假设我有几个查阅表中并没有真正依赖于任何东西,如CountryCodes,ColorsCodes,SomethingElseCodes.我可能会用它们来填充下拉或其他任何原因.这些独立的存储库吗?它们可以组合成某种逻辑分组/存储库,例如CodesRepository?或者是针对最佳做法的.

ami*_*t_g 11

您可以在存储库中拥有所需的任何方法:)在您提及的两种情况下,返回填充了电话列表的用户是有意义的.通常,用户对象不会完全填充所有子信息(例如所有地址,电话号码),并且我们可能有不同的方法来获取用不同类型的信息填充的用户对象.这被称为延迟加载.

User GetUserDetailsWithPhones()
{
    // Populate User along with Phones
}
Run Code Online (Sandbox Code Playgroud)

对于更新,在这种情况下,用户正在更新,而不是电话号码本身.存储模型可能会将手机存储在不同的表中,这样您可能会认为只是手机正在更新,但如果您从DDD角度考虑,则情况并非如此.就可读性而言,虽然是行

UserRepository.Update(user)
Run Code Online (Sandbox Code Playgroud)

单独不传达正在更新的内容,上面的代码将清楚地说明正在更新的内容.此外,它很可能是前端方法调用的一部分,可能会显示正在更新的内容.

对于查找表,实际上甚至是其他情况,使用GenericRepository并使用它是很有用的.自定义存储库可以从GenericRepository继承.

public class UserRepository : GenericRepository<User>
{
    IEnumerable<User> GetUserByCustomCriteria()
    {
    }

    User GetUserDetailsWithPhones()
    {
        // Populate User along with Phones
    }

    User GetUserDetailsWithAllSubInfo()
    {
        // Populate User along with all sub information e.g. phones, addresses etc.
    }
}
Run Code Online (Sandbox Code Playgroud)

搜索通用存储库实体框架,你会很好的实现.使用其中之一或编写自己的.

  • 不幸的是这个答案是错的.应将存储库视为内存中对象的集合,您应该避免延迟加载.这是关于http://besnikgeek.blogspot.com/2010/07/specification-pattern-and-complex.html的好文章 (2认同)

Dar*_*wis 9

您在Aggregate Root存储库中的示例是完全正确的,即任何不能合理地存在而不依赖于另一个的实体都不应该拥有自己的存储库(在您的情况下是Phone).如果不考虑这一点,您可以在1-1映射到数据库表中快速找到自己的存储库.

您应该考虑使用工作单元模式进行数据更改而不是存储库本身,因为我认为在将更改持久保存到数据库时,它们会导致您对意图产生一些混淆.在EF解决方案中,工作单元本质上是围绕EF上下文的接口包装器.

关于查找数据的存储库,我们只需创建一个ReferenceDataRepository,负责处理不属于域实体的数据(国家,颜色等).


Arn*_*psa 5

如果手机对用户没有意义,那么它就是一个实体(如果您关心它的身份)或价值对象,应该始终通过用户进行修改并一起检索/更新.

将聚合根视为上下文定义器 - 它们绘制局部上下文,但在全局上下文(您的应用程序)本身.

如果您遵循域驱动设计,则每个聚合根应该是1:1的存储库.
没有理由.

我打赌这些是你所面临的问题:

  • 技术难点 - 物体关系阻抗不匹配.您正在努力维护整个对象图,而实体框架则无法提供帮助.
  • 域模型是以数据为中心的(与行为为中心相对).因为那样 - 你失去了关于对象层次结构(前面提到的上下文)的知识,并且神奇地一切都变成了聚合根.

我不确定如何解决第一个问题,但我注意到修复第二个问题首先修好了.要了解我以行为为中心的含义,请试一试本文.

Ps减少存储库以聚合根是没有意义的.
Pps避免"CodeRepositories".这导致以数据为中心 - >程序代码.
Ppps避免工作单元模式.聚合根应该定义事务边界.