如何使用域驱动设计避免具有非常大的对象

我们正在遵循领域驱动设计来实现大型网站。

但是,通过将行为放在域对象上,我们最终会得到一些非常大的类。

例如,在我们的网站用户对象上,我们有许多许多方法 - 例如处理密码,订单历史记录,退款,客户细分。所有这些方法都与用户直接相关。其中许多方法在内部委托给其他子对象,但这
仍然会导致一些非常大的类。

我热衷于避免公开大量子对象,例如user.getOrderHistory().getLatestOrder()。

还有哪些其他策略可以避免这个问题?


答案 1

您看到的问题不是由领域驱动设计引起的,而是由于缺乏关注点分离。领域驱动设计不仅仅是将数据和行为放在一起。

我建议的第一件事是花一天左右的时间阅读《领域驱动设计》,从Info-Q免费下载。这将概述不同类型的域对象:实体、值对象、服务、存储库和工厂。

我建议的第二件事是阅读单一责任原则

我建议的第三件事是,你开始沉浸在测试驱动开发中。虽然通过先编写测试来学习设计并不一定能让你的设计变得很棒,但它们往往会引导你进行松散耦合的设计,并更早地揭示设计问题。

在你提供的例子中,WebsiteUser肯定有太多的责任。实际上,您可能根本不需要,因为用户通常由 .WebsiteUserISecurityPrincipal

由于缺乏业务环境,很难建议你应该如何处理你的设计,但我首先建议通过创建一些索引卡来代表你在系统中的每个主要名词(例如,客户,订单,收据,产品等),做一些头脑风暴。在顶部写下候选人的班级名称,您认为左侧的班级固有的职责,以及右侧将与之合作的班级。如果某些行为感觉不属于任何对象,则它可能是一个很好的服务候选(即身份验证服务)。把牌摊开在桌子上,和你的大学讨论。不过,不要过多地谈论这一点,因为这实际上只是一个头脑风暴的设计练习。有时这样做可能比使用白板容易一些,因为您可以移动东西。

从长远来看,你真的应该拿起埃里克·埃文斯(Eric Evans)的《领域驱动设计》(Domain Driven Design)一书。这是一本大读物,但非常值得你花时间。我还建议您根据自己的语言偏好选择敏捷软件开发,原则,模式和实践C#中的敏捷原则,模式和实践


答案 2

虽然真正的人类有很多责任,但你正在走向上帝对象的反模式

正如其他人所暗示的那样,您应该将这些职责提取到单独的存储库和/或域服务中。例如:

SecurityService.Authenticate(credentials, customer)
OrderRepository.GetOrderHistoryFor(Customer)
RefundsService.StartRefundProcess(order)

具体说明命名约定(即使用 OrderRepositoryOrderService,而不是 OrderManager)

由于方便起见,您遇到了此问题。也就是说,将 a 视为聚合根并通过它访问所有内容很方便。WebsiteUser

如果你更强调清晰度而不是便利性,它应该有助于区分这些问题。不幸的是,这确实意味着团队成员现在必须了解新服务。

另一种思考方式:就像实体不应该执行自己的持久性(这就是我们使用存储库的原因)样,你不应该处理退款/细分/等。WebsiteUser

希望有所帮助!