我目前为数据库中的几乎每个表都有一个存储库,并希望通过将它们减少到仅聚合根来进一步与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]);聚合根的概念在理论上比在实践中更容易理解。我永远不会有不属于某个用户的电话号码,那么取消PhoneRepository并将与电话相关的方法合并到UserRepository中是否有意义?假设答案是肯定的,我将重写前面的代码示例。
我可以在UserRepository上使用返回电话号码的方法吗?或者它应该总是返回对用户的引用,然后遍历用户的关系以获得电话号码:
List<Phone> phones = UserRepository.GetPhoneNumbers(userId);
// Or
User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone不管我用哪种方式获取手机,假设我修改了其中的一个,我该如何更新它们呢?我有限的理解是,根目录下的对象应该通过根目录进行更新,这将引导我选择下面的选项#1。虽然这在Entity Framework中工作得很好,但这似乎非常不具描述性,因为阅读代码时我不知道我实际在更新什么,即使Entity Framework在图中记录了更改的对象。
UserRepository.Update(user);
// Or
UserRepository.UpdatePhone(phone);最后,假设我有几个查找表,它们并没有真正绑定到任何东西,比如CountryCodes、ColorsCodes、SomethingElseCodes。我可能会使用它们来填充下拉列表,或者出于其他任何原因。这些是独立的存储库吗?它们是否可以组合成某种逻辑分组/存储库,如CodesRepository?或者这违反了最佳实践。
发布于 2011-03-02 03:11:43
您可以在存储库中使用任何您想要的方法:)在您提到的两种情况下,返回填充了电话列表的用户是有意义的。通常情况下,user对象不会完全填充所有子信息(比如所有地址、电话号码),我们可能有不同的方法来使用不同种类的信息填充user对象。这被称为延迟加载。
User GetUserDetailsWithPhones()
{
// Populate User along with Phones
}对于更新,在这种情况下,更新的是用户,而不是电话号码本身。存储模型可能会将手机存储在不同的表中,这样你可能会认为只有手机在更新,但如果你从DDD的角度考虑,情况并非如此。就可读性而言,虽然行
UserRepository.Update(user)单独并不能传达正在更新的内容,它上面的代码可以清楚地说明正在更新的内容。此外,它很可能是前端方法调用的一部分,可能表示正在更新的内容。
对于查找表,实际上甚至在其他方面,拥有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.
}
}搜索Generic Repository Entity Framework,你会发现很多不错的实现。使用其中的一个或编写您自己的。
发布于 2011-03-02 03:28:33
您在聚合根存储库上的示例是完全正确的,即任何实体不能在不依赖于另一个实体的情况下合理存在,就不应该有自己的存储库(在您的情况下是电话)。如果不考虑这一点,你很快就会发现自己在数据库表的一对一映射中拥有大量的存储库。
您应该考虑使用工作单元模式进行数据更改,而不是存储库本身,因为我认为当涉及到将更改持久化回数据库时,它们会给您带来一些关于意图的混乱。在EF解决方案中,工作单元本质上是EF上下文的接口包装器。
对于您的查找数据存储库,我们只需创建一个ReferenceDataRepository,它将负责不属于某个领域实体(国家、颜色等)的数据。
发布于 2016-09-21 04:44:09
如果Phone实体只有与聚合根用户一起才有意义,那么我认为添加新的Phone记录的操作是通过特定方法(DDD行为)由用户域对象负责的,这也是有意义的,原因有几个,直接的原因是我们应该检查User对象是否存在,因为Phone实体依赖于它的存在,并且可能在执行更多验证检查的同时对其保持事务锁定,以确保在我们完成验证操作之前没有其他进程删除根聚合。在其他情况下,对于其他类型的根聚合,您可能希望聚合或计算一些值,并将其持久化在根聚合的列属性上,以便稍后由其他操作进行更有效的处理。注虽然我建议用户域对象有一个添加电话的方法,但这并不意味着它应该知道数据库或EF的存在,EM和Hibernate的一个伟大功能是它们可以透明地跟踪对实体类所做的更改,这也意味着通过它们的导航集合属性添加新的相关实体。
此外,如果您想使用检索所有电话的方法,而不管拥有它们的用户是谁,您仍然可以通过用户存储库,只需要一个方法将所有用户作为IQueryable返回,然后您可以将它们映射到获取所有用户电话,并对其进行精细化查询。因此,在这种情况下,您甚至不需要PhoneRepository。此外,如果我想在方法后面抽象查询,我更喜欢为IQueryable使用一个带有扩展方法的类,它可以在任何地方使用,而不仅仅是从Repository类中使用。
为了能够仅使用域对象而不是电话存储库来删除电话实体,您需要确保UserId是电话主键的一部分,或者换句话说,电话记录的主键是由UserId和电话实体中的一些其他属性(我建议使用自动生成的身份)组成的组合键。这很直观,因为电话记录是由用户记录“拥有”的,从用户导航集合中删除它等同于从数据库中完全删除。
https://stackoverflow.com/questions/5158064
复制相似问题