关注【索引目录】服务号,更多精彩内容等你来探索!
它更像是一位正在重新学习领域驱动设计 (DDD) 的人写的一篇(并非那么)深入的探讨。如果你曾经盯着一张满是聚合、存储库和有界上下文的图表,心想:“等等……这到底是怎么回事?” 那么欢迎,你并不孤单。
这篇文章是我的方式:
-
重建 DDD 的坚实心理模型。 -
将抽象概念与(希望)直观的类比联系起来。 -
分享令我感动的事情,尤其是通过更具对话性和个性化的视角。
无论您是像我一样刚刚开始学习,还是第一次探索 DDD,我都希望本指南能够拨开迷雾,激发好奇心,让旅程更加愉快。
DDD 的核心概念
我们开始了。首先,领域驱动设计 (DDD) 是一种战略性的软件设计方法,专注于通过丰富、有意义的代码来反映业务逻辑,从而对复杂的领域进行建模。无论你是在团队中,还是独自完成一个大型项目(愿上帝保佑你),你都应该对自己说:“在我们(或我)开始编码之前,请确保我们理解了我们在构建什么以及为什么构建它。”
从我(重新学习)的角度来看,DDD 是:
- 一种将开发
与领域专家或真正了解业务的人员重新联系起来的方法。 - 一种重视代码边界和表现力
的方法(你一会儿就会明白我指的是什么) -
一种要求团队在代码、业务对话和文档中使用通用语言或一致术语的结构。
DDD中的分层架构
DDD 不仅仅关乎建模,也关乎组织。大多数企业级应用程序受益于分层架构,这种架构能够分离职责并帮助管理复杂性。这些层是逻辑上的,而非物理上的。所以……这意味着它们可以帮助您理解和构建系统,但不一定能映射您的部署方式。
这些层的常见表示是:
1.领域模型层
系统的核心,业务逻辑所在。DDD 的关键构建块(实体、聚合、值对象等)就驻留在此层。
这一层包含定义系统行为的基本元素,并强制执行使领域有意义的规则。它与基础设施完全隔离,这是设计使然。在这里,您的软件可以像业务一样思考。
让我们来认识一下这里的基石:
实体 - 可识别的老兵
实体由一个唯一的身份(如或)定义,即使其属性发生变化,该身份也会随时间持续存在。orderIduserId
例子:
User:有 userId,可能会更新他们的电子邮件Order:由 标识 orderId,即使其项目发生变化
你可以把它想象成......一个拥有护照的旅行者,无论穿什么衣服或去哪里,他们的身份都保持一致且可追踪。
价值对象 - 优雅的极简主义者
值对象保存不可变且可互换的数据。两个具有相同属性的值对象相等。
例子:
Address:由 street、city和组成zipCodeMoney:与 amount和currency
你可以把它想象成……一块油漆样本,仅由颜色和属性定义,而非身份。易于复制,可安全丢弃。
聚合 - 凝聚力集团
聚合是一组作为单个单元工作的实体和值对象。所有访问和更新都通过聚合根进行。
例子:
Order总计可能包括 OrderItems,ShippingDetails和TotalAmount。
您可以将其想象为……乐高积木,单个积木只有在按照一个明确的目的进行组装时才有意义。
聚合根 - 明智的守门人
聚合根是聚合的主要入口点。它强制执行规则并确保一致性。你永远不会直接接触内部对象,而是必须通过屋顶。
例子:
Order是管理其项目、状态、支付流程并确保遵守业务规则的根源。
你可以把它想象成......学校校长,接触员工或记录,通过这一权力来确保秩序和完整性。
领域活动 - 大声播音员
领域事件表示领域内发生了一些有意义的事情。它允许系统在非紧密耦合的情况下做出反应。
例子:
OrderPlaced可以触发通知服务、付款处理或装运安排。
你可以把它想象成……镇上的公告员,大声宣布新闻,以便任何听到的人都可以决定下一步该做什么。
存储库 - 谨慎的僚机
Repository抽象了聚合的数据存储逻辑。它允许您检索、保存和删除域对象,而无需了解持久化的具体工作原理。
例子:
OrderRepository处理类似 save(order)或的方法findById(orderId)
你可以把它想象成……你的后台助理,你可以要求某些东西,它会默默地获取或存储它,而不会打扰演出。
2. 应用层
负责协调业务用例的组件,它不包含业务规则,但知道谁应该执行什么。它处理来自 UI 或外部系统的传入请求,然后将这些请求转换为领域模型的操作,通常使用已经为演示而构建的 DTO 或 ViewModel。
它从不验证业务规则或直接与数据库交互,而是依赖于存储库。
一个很好的例子如下:
假设用户在您的网站上点击“下订单”:
-
表示层发送数据 -> PlaceOrderCommand -
应用层接收命令 -
它要求 OrderAggregate处理创建(借助域实体和规则) -
成功后,它将返回一个用于显示的 ViewModel。
3.基础设施层
我们可以称其为“幕后修复程序”,它实用但不引人注目。它支持所有其他持久化、网络和外部服务,提供对数据库、外部 API、消息队列、文件系统的访问,实际上就是系统与外界交互所需的一切。
它绝不应该泄露到领域中。相反,它应该通过诸如存储库、事件调度器或服务接口之类的抽象来谨慎地为领域提供服务。
4. 表示层
用户所看到和与之交互的内容,通过 ViewModel 与应用层进行通信。该层负责提供用户界面 (UI)、处理输入并呈现输出。它通过 DTO 或 ViewModel 与应用层通信,确保只有有效且结构化的数据才能深入系统。
它负责通过视图、模板和控制器(例如在 MVC 框架中)显示用户界面。它还会在将输入传递给应用层之前对其进行格式化和验证。
何时(以及何时不)使用 DDD?
说实话:DDD 并非适用于所有项目。诚然,它在逻辑不断发展、多个团队需要保持一致的复杂领域中大放异彩,而且它也挺酷的(至少在我看来,在 IDE 中看到所有这些文件夹并知道在哪里查找错误,这感觉有点奇怪)。但如果你尝试将 DDD 用于周末待办事项列表应用……那还不如用火箭发射器猎兔子呢。
那么我们该如何决定呢?
您应该查看清单并查看项目是否符合以下任何一项:
-
您正在处理复杂的业务规则,并且需要正确地表示现实世界的逻辑。 -
您的领域将随着时间的推移而发展,例如新规则、新行为、新用例。 -
您希望系统不同部分之间有明确的界限 -
多个团队需要使用相同的语言,代码和业务术语必须保持一致。 -
您构建的目的是为了长期的可维护性,而不是短期的速度。
另一方面,如果您的项目或应用程序是……
-
主要是CRUD(创建、读取、更新、删除),领域复杂性较低 -
快速 MVP 或概念验证 -
一个单独的项目,需要快速迭代,而不是分层组织
... DDD 可能有点过度了。
话虽如此,即使在比较简单的项目中,您也可以完全应用 DDD 原则,而无需采用完整的架构。定义清晰的边界或避免基础架构泄漏到核心逻辑中,可以带来很大的不同。这不是一个“是”或“否”的问题,而是一个范围。您可以逐步采用 DDD 的部分功能,并让系统结构随着复杂性的需求而增长。
总结
好了,以上就是领域驱动设计的完整演练,它并非来自象牙塔般的理论,而是从一个将一切重新拼凑起来的人的视角。每一层都有其角色,每个概念都有其表达方式,现在希望 IDE 中的每个文件夹都有其存在的意义,而不仅仅是“感觉井井有条”。
这种方法并非追求完美,而是注重系统化思考,用业务语言进行交流,并有目的地进行构建。无论你决定完全拥抱 DDD,还是仅仅借鉴其最佳理念,目标都是一样的:编写合理的软件。
感谢您一路读到这里,如果您有任何想法、指正,或者您对 DDD 有什么自己的看法,请在评论区告诉我!我们都在这里学习。
关注【索引目录】服务号,更多精彩内容等你来探索!

