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

Pab*_*jim 28 .net java oop design-patterns domain-driven-design

我们正在关注领域驱动设计以实现大型网站.

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

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

我很想避免暴露很多子对象,例如user.getOrderHistory().getLatestOrder().

可以使用哪些其他策略来避免这些问题?

Der*_*eer 19

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

我建议的第一件事是花一天左右的时间阅读Domain Driven Design,可以从Info-Q免费下载.这将概述不同类型的域对象:实体,值对象,服务,存储库和工厂.

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

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

在您提供的示例中,WebsiteUser肯定有太多的责任.实际上,您可能根本不需要,WebsiteUser因为用户通常由一个用户代表ISecurityPrincipal.

鉴于缺乏业务背景,有点难以确切地说明你应如何处理你的设计,但我首先建议通过创建一些代表你系统中每个主要名词的索引卡来进行一些脑力激荡(例如,客户,订单,收据,产品等).在顶部写下候选班级名称,你认为左边的班级所固有的责任,以及它将与右边合作的班级.如果某些行为感觉不属于任何对象,那么它可能是一个很好的服务候选者(即AuthenticationService).与你的大学一起在桌子上传播卡片并进行讨论.不要过分夸大其实,因为这实际上只是作为头脑风暴的设计练习.

从长远来看,你应该选择Eric Evans的Domain Driven Design这本书.这是一个很好的阅读,但值得你花时间.我还建议您根据自己的语言偏好,选择C#中的 敏捷软件开发,原则,模式和实践敏捷原则,模式和实践.


Vij*_*tel 10

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

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

SecurityService.Authenticate(credentials, customer)
OrderRepository.GetOrderHistoryFor(Customer)
RefundsService.StartRefundProcess(order)
Run Code Online (Sandbox Code Playgroud)

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

你因为方便而遇到了这个问题.即,将a WebsiteUser视为聚合根并通过它访问所有内容很方便.

如果你更注重清晰度而不是方便,那么它应该有助于区分这些问题.不幸的是,这确实意味着团队成员现在必须知道新的服务.

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

希望有所帮助!


Ope*_*rce 6

要遵循的一个非常简单的经验法则是“类中的大多数方法必须使用类中的大多数实例变量”——如果您遵循此规则,类将自动具有正确的大小。


tst*_*ter 2

我遇到了同样的问题,我发现使用子“管理器”对象是我们案例中的最佳解决方案。

例如,在您的情况下,您可能有:

User u = ...;
OrderHistoryManager histMan = user.getOrderHistoryManager();
Run Code Online (Sandbox Code Playgroud)

然后你就可以使用 histMan 来做任何你想要的事情。显然你想到了这一点,但我不知道你为什么要避免它。当你的对象看起来做了太多事情时,它会分离关注点。

这样想吧。如果您有一个“人类”对象,并且您必须实现该chew()方法。你会把它放在Human对象上还是Mouth子对象上。