适当的仓库设计,将一个对象附加到另一个对象

3

为了保持良好的存储库/控制反转(Repository/IoC)设计,我正在尝试找出当前问题的最佳方法。

有两个对象,即MemberCharacter

基本上,一个成员可以拥有多个角色。很简单。

Member
{
 IList<Character> Characters { get; set; }
}

There is an IMemberRepository...

interface IMemberRepository
{
 MemberCreateStatus CreateMember(string email, string password);
 bool ValidateMember(string email, string password);
 bool ChangePassword(string email, string password, string newPassword);
 void RecoverPassword(string email);
}

这里有一个 ICharacterRepository 接口。

interface ICharacterRepository
{
 Character CreateCharacter(string name);
}

那么,我的问题很简单。我在哪里添加逻辑来将Character添加到Member中?我应该在IMemberRepository中吗?对我来说,这似乎很麻烦。它似乎超出了IMemberRepository的任务范围。我应该将其添加到ICharacterRepository中吗?这也对我来说有点奇怪,因为给予知识违反了保持分离的原则。在这种情况下,最好的方法是什么?
如果只有这一个方法,那就不是太大的问题 - 但是在两个类之间还会发生很多其他事情。如何处理两个相互关联的对象的情况符合“标准”方式?我正在使用ASP.NET MVC进行编码。
3个回答

4

我认为你对Repositories的理解可能与其本意有所不同,这可能是你困惑的原因之一。

Repositories用于为一组数据提供集合语义。这意味着Repositories可以获取、添加和删除内容,但它们不进行验证,也不更改单个项的内容。虽然最近许多人将某些内容命名为Repository并使其看起来像DAO,但它们并不是数据访问对象(DAO)。

你的Member包含一个Characters列表。太好了!想要将一个Character添加到Member中吗?就按照这样做:Member.AddCharacter(...)Member.Characters.Add(...)。我通常使用前者,但后者也可以(但需要自定义集合实现)。


你的代码库不会验证成员本身,它会从数据库中检索“Member”数据并将其返回给业务层。然后,你的业务层将验证密码是否正确(或确保存储库返回了记录)。 - KallDrexx
@qstarin:抱歉,我不知道你刚才说了什么。 - Ciel
仓储库具有ISessionFactory。领域对象没有ORM的概念。我正在使用nHibernate。仓储库确实使用UnitOfWork抽象来处理更改。这就是问题所在。你告诉我的方法不可能按照你告诉我的方式去做。领域对象不知道ORM是什么。他们根本没有这个概念。但是nHibernate使用代理和延迟加载。我不能够RetrieveMember,然后自由地使用它的方法。我必须在仓储库的UnitOfWork中完成。 - Ciel
那么我只在添加/删除时使用存储库吗?那么在哪里可以使用数据访问逻辑来执行更复杂的任务呢? - Ciel
1
这已经变成了一个不太适合在这里讨论的长时间讨论,而且我认为我们有些误解,尽管我现在更好地理解了你的情况。我现在也非常忙。但是,是的,我发现ORM懒加载引入了这些复杂性,因此我避免使用它。也许在您的情况下这是不可能的。事件可以用来潜在地解决这个问题。调用add方法并没有实际添加该项,而是引发一个事件,表示已添加角色,并且处理程序负责提取聚合并添加。 - quentin-starin
显示剩余10条评论

1

我认为IMemberRepository的责任是填充MemberCharacters。以下是几个原因:

  • 由于IMemberRepositoryMember类型的Repository,它知道Member由什么组成。这就是它了解Character类型的原因。
  • 这绝对不是ICharacterRepository的责任。将来Character类型可能用于组合其他类型。每当添加新类型时,都会影响CharacterRepository
  • IMemberRepository将使用ICharacterRepository来填充Member中的Character对象。

0

对我而言,最自然的方法是将该逻辑放在 Member 类内部,因为 Member 对象负责跟踪其自己的字符。

您认为呢?


SoMos - 在我看来,这不是最灵活的做事方式。我可以看到最小阻力的路径会导致这种方法。然而,你应该有一个接口,基于它来定义你的成员属性,以及一个通用的T存储库用于基本的保存/检索。然后,你将拥有一个泛型类型的“服务或任务”类,用于执行给定类实体之外的任何操作。这就是我的简化方法... - jim tollan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接