【问题标题】:Role-Specific Repository vs Specific Repository特定于角色的存储库与特定存储库
【发布时间】:2013-01-08 20:18:33
【问题描述】:

在我发现这个article 之前,我不知道角色特定的存储库:

而不是一个包罗万象的存储库,它公开了 sun,我们也可以应用接口隔离原则 对于基于角色的接口,并定义只公开什么的接口 一类需要。

public interface IProductRepositoryForNewOrder
{
    Product[] FindDiscontinuedProducts();
}

单个存储库实现实现所有产品存储库 接口,但只有所需的单个方法被公开和使用 调用者。

a) 两者的区别在于 Specific Repositories 我们每个 Aggregate Root 有一个特定的合约,而 Role-Specific Repositories 我们可以有多个合约Aggregate Root,这些合约中的每一个都是针对在 Aggregate Root 上运行的特定调用者的需求量身定制的?

b) 在您看来,这两种模式各自的优缺点是什么?

谢谢

更新:

昨天我发现了你的answers 之一,你认为应该使用角色特定的存储库模式:

“另一种选择是使用 lambda 代替 OrdersSelectorService。 如果 lambda 在您的语言中不可用,那么它应该是 界面。通过 OrderRepository 的好处是基于 接口隔离原则,其目标是减少 不必要的耦合。对客户需求的行为不太可能 OrderRepository 上的所有方法,相反它需要一个特定的 函数,所以要明确。”

为什么在上面的摘录中你提倡使用角色特定的存储库模式,但在这里你似乎建议只在特殊情况下使用它。另一个主题中的示例是否是特殊情况(除此之外 - 我绝不是说您在自相矛盾,我只是看不出这两个示例在使用或不使用角色特定模式方面有何不同) ?

【问题讨论】:

    标签: design-patterns domain-driven-design repository-pattern


    【解决方案1】:

    a) 是的。这就是接口隔离原则。明确角色/用例。好处是减少和“清理”依赖关系。

    b) 对我来说,角色特定方法的主要缺点是接口的激增以及由此导致的接线、引用等的增加。然而,这种缺点可以被视为不是由于原理上的缺陷,而是在编程语言中更是如此。在诸如 F# 之类的函数式语言中,由于函数而非接口的激增,接口隔离是默认方法。从某种意义上说,函数是一种“更锋利”的工具。

    非角色特定方法的优点在于,它可以被视为定义数据访问契约的单一语言元素、接口或类。在某些情况下,从技术角度评估架构是很有价值的。

    【讨论】:

    • 1 - “在某些情况下,从技术角度评估架构很有价值” 意思是如果我使用 C#,则使用非角色特定的方法,如果我是使用说 F#,然后使用特定于角色的方法? 2 - 我假设您在使用 C# 时更喜欢非角色特定的方法?
    • 我的意思是,查看与聚合相关的整个数据访问合同(整个存储库接口)可能很有价值。我也在 C# 中使用角色特定的方法,它只是有一些缺点。
    • 在这种情况下,重点是实体,而不是存储库。存储库可以以特定于角色的方式实现,也可以不实现。所以是的,我提倡接口隔离,但在实体级别,而不是存储库级别。 ISP 是否申请存储库是这个问题的主题,正如您所见,有正面和负面。
    • 在我看来,差异非常微妙,这最终可能是一个偏好问题,因为这两种方法都不是绝对错误的。我在实体案例中通过提供表示为 lambda 的显式依赖项来使用 ISP 的原因是因为 lambda 不需要单独的声明,因此它们不会“污染”域层。此外,在 IMO 中,实体级别的显式比存储库级别的显式更重要,这是一个技术性问题。
    • 使用 IOrderSelector 也是可以找到的,但可能是不必要的,因为 lambda 将表达相同的合同而无需声明额外的接口。我所说的“更多技术问题”的意思是,从您的领域的角度来看,持久性是一个技术问题。 DDD 的部分想法是促进functional cohesion,这基本上相当于在代码中清楚地表达您的域,因此 POCO、持久性无知等。作为持久性一部分的存储库不如域“有趣”..
    【解决方案2】:

    我完全支持 SOLID 代码,但接口隔离原则确实有其局限性,尤其是在 DDD 上下文中。

    [挑剔模式]

    如果你在这封信中应用了 ISP,你几乎可以从文章中获取关于存储库的声明,然后稍微修改一下

    使用域实体的类很少使用其中的所有方法。

    因此,对于每个领域实体,你应该创建与实体的客户端一样多的接口,每个接口中只包含与客户端相关的方法,并让实体实现这些接口。

    当然,这是荒谬的,没有人会这样做。但是,嘿,ISP 应该是一个通用的 OO 概念,不是吗?

    [/ nitpicker 模式]

    现在,如果我们回顾一下 ISP 存在的最初原因,它应该与胖接口(即不具有凝聚力的接口)作斗争。但是存储库本身不是有凝聚力的吗?它不是一个基本的、原子的 DDD 构建块吗?它是否值得分成几十个迷你查询对象?此外,我们领域中的每个类不应该与通用语言对齐吗?对于ProductRepoInterfaceForClient1ProductRepoInterfaceForClient2ProductRepoInterfaceForClient3 等接口,情况并非如此……

    不要误会我的意思,ISP 仍然很有用,尤其是作为一种检测接口合同何时比应有的更加异构的方法。鲍勃叔叔在 ISP 上的original paper 有很好的例子 - 请参阅“接口污染”段落和 ATM 示例。

    但是一旦达到了合理的内聚水平,ISP 就不应盲目应用 IMO,尤其是当它与基本 DDD 原则相冲突时,或者在您的代码库中充斥着数百个接口,从而成为维护的噩梦。

    【讨论】:

    • 您是否也反对使用特定存储库模式?
    • +1 表示与基本 DDD 原则的冲突和维护噩梦。有界上下文的想法消除了对 ISP 的需求。
    • @user437291 不,我不是,我通常按照public class ProductRepository : NHibernateRepository<Product>, IProductRepository 的方式定义我的存储库,其中IProductRepository 在域层中定义,NHibernateRepository<T> 在基础结构层中定义。跨度>
    • 我假设 NHibernateRepository 实现了通用存储库 IRepository
    • 是的,如果您想要除 NHibernate 之外的其他持久性机制,您可以这样做。但是如果你只需要 NHibernate 和 mock 存储库(用于测试),你几乎可以不用它,因为 IProductRepository 已经为你的 mock 提供了一个完美的接口。
    猜你喜欢
    • 2011-10-19
    • 1970-01-01
    • 2011-11-06
    • 2021-07-07
    • 2011-10-17
    • 1970-01-01
    • 2019-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多