黑客新闻对DDD的各种吐槽和经验分享



著名DDD专家mathiasverraes的定义了什么是领域驱动设计(DDD)?,在黑客新闻引起各种讨论
  
LennyWhiteJr 
在这里看到很多人对 DDD 的仇恨,所以让我从在我的团队中倡导 DDD 的人那里提供另一种观点。
当我加入时,我的团队已经为我们应用程序的第一个版本构建后端大约 4 个月。当我加入时,那时代码库是Martin Fowler 定义的“贫血领域模型”。

  • - 在最松散的意义上有一个“域模型”
  • - 每个模型类型只是一个 POJO,每个字段都有原始的 getter/setter
  • - 几乎所有字段都是原始类型(主要是带有一些 int/doubles/dates 的字符串)
  • - 所有验证这些类型的代码在应用程序代码中完成,并在整个代码库中分散
  • - 内部数据库标识符完全暴露在代码模型中 - 内部服务类型与外部服务类型自由混合 - 没有聚合根的概念 - 每个实体都被访问特别指定

这是一种非常不可持续的方法,我做的第一件事就是尝试在最痛苦的领域实施战略性 DDD。这包括

  • - 采用富值对象来表示领域概念而不是原始字符串
  • - 在模型类中实施业务不变量
  • - 使用匹配业务行为和执行验证的方法丰富领域实体
  • - 创建存储库,将大部分持久性细节移出应用程序代码
  • - 根据我们所需的访问模式定义聚合,这简化了我们的数据访问
  • - 内部域的有界上下文
  • - 将外部服务类型映射到我们自己的内部表示

所有这一切的结果是创建了一个核心“域模型”,它捕获了我们服务的预期业务行为,最重要的是,最终显着简化了应用程序代码的其余部分。如果 DDD 使您的应用程序代码更加复杂,那么您就做错了。
 

Fiahil 
几年前我参加了几次 DDD 聚会,现在为什么要参与这种架构一直没有意义。房间里挤满了具有深厚企业软件 (tm) 知识的经验丰富的 Java 开发人员。
然后,我开始担任 AI 软件工程师(软件工程师 + DevOps + 数据科学家的组合),一切顺利。DDD 是与数据科学、人工智能、机器学习相关的任何事物的绝妙设计模式。为什么 ?因为您 90% 的问题都是追溯有意义的,组织、排序、过滤、聚合您从最喜欢的数据库/湖泊/仓库中获得的所有数据。DDD 让您在现有的业务挑战和正在其上运行的分析之间拥有统一的语言、不变的定义和期望。它非常适合验证跨数据集的假设,例如:销售额永远不能 < 0 ?让我们检查一下... 哦,好吧,您忘记了,请返回,所以现在您可以定义它们并明确何时包含或排除它们。
 
softveda
我是从一名程序员开始22 年软件职业生涯,我只见过一次DDD成功且恰当地使用 。所有其他的尝试都是半成品和过度设计的混乱。
它曾经是一个非常复杂的保单管理领域。它具有高度复杂的业务规则,并且必须维护不变量。最初的开发人员都是咨询公司的资深开发人员。在交接之前,我作为团队的永久成员加入了。
问题是,几年来很难让新员工快速维护复杂的代码并对其进行测试。此外,随着新任 CEO、中小企业退休、市场变化等,即使是无处不在的语言也开始发生变化。例如,会员成为客户。现在,您要么重构整个代码库,要么使用与新业务术语不同的代码。猜猜选了哪一个?随着时间的推移,它变得难以重构。我现在已经离开公司很多年了,但我假设这段代码仍然存在。
然后我在一些企业工作,这些核心系统建立在像 SAP 这样的庞大单体中,维护着我来自 WITCH 公司的人员队伍。所有的数字应用程序、门户网站等都是前端、API 和缓存层。DDD 的复杂性在 SAP 或 PEGA 或 Dynamics 等的内部。
 

ToJans 
随着 DDD 越来越流行,出现了很多“初学者专家”和自称为思想领袖的人……(~= 敏捷运动)
如果您正在学习 DDD,CQRS-all-the-things 通常是您要经历的一个阶段。
在我个人看来,检测“初学者专家”的一个很好的启发是他们更喜欢使用大量 DDD 术语,并且专注于更多技术方面,而不是真正尝试首先了解业务领域。
不幸的是,没有一个人人都接受的 DDD 定义。DDD 的核心是关于软件设计的实践,它把领域——用户语言和问题——放在首位。没有技术考虑。
ES (Event Sourcing) 和 CQRS 是人们在做 DDD 时喜欢使用的技术模式,因为各种原因,但它们绝不是实践 DDD 所必需的。
 
amw
我不提倡 CQRS,我个人从未在项目中使用过它。但是,架构与 DDD 相关联的原因是 DDD 从根本上是一种哲学或一种想法。某些软件设计比其他软件设计更好地实现了这一哲学理想。
例如,DDD 强调“隔离域模型”的想法 - 这有一个非常实际的原因,因为您的业务规则已经足够复杂,因此混合了数据库事务、HTTP 缓存、响应序列化、授权等。 , 进入您的域逻辑使其更难理解。您希望能够与客户/领域专家进行对话,在那里他们解释某些内容,并且您可以快速调整代码以满足他们的需求。至少这是理想的目标。
因此,某些模式可以更好地实现该目标。例如,这意味着您需要某种数据访问层模式,以便关心数据库的代码与关心域逻辑的代码分开。嗯,有上百万种不同的方法来实现这种分离,它们有不同的权衡,就像其他一切一样,某些模式变得流行。这就是 CQRS 正在发生的事情。人们认为它可以更好地表达领域模型,因为创建数据和查询数据通常与客户的观点截然不同。
这个目标是否实现,我不确定。但正如我所见,这就是人们尝试这种模式的原因。
 

necovek 
我的经验是,不幸的是,大多数开发人员要么没有足够的经验,要么不够聪明(这些密切相关),无法阻止抽象蔓延并仅应用有意义的抽象。而其他人根本就没有足够的兴趣,他们知道他们可以通过以下错误判断的模式获得 LGTM。
他们还抓住机会使用 DDD 书中的相同术语,然后继续用每个人都能理解的更通俗的术语来解释它们:就像社会科学又一次(对不起,社会科学,但它就是这样)他们发明了术语,所以他们会更“科学”。为什么不简单地使用每个人都理解的术语?
准确的说大概是。每个领域都会在某个时候发展出一种行话。它可以用来排除或听起来很聪明,但大多数时候它只是为了精确。常用术语过于模糊/多义。
 
DDD 有时让我想到敏捷的软件版本。背后的想法和哲学很好,但最终被视为灵丹妙药。如果您这样做,您将拥有一个良好的架构,并且您的软件将得到良好的架构。特别是在 .NET 世界中,我看到 DDD 与 CQRS 一起被标记为“干净的架构”,实际上它是一堆层和分离。
DDD 与其说是制作软件,不如说是管理您的业务软件需求。如果您阅读“实施 DDD”是一件好事,那么您应该将精力集中在您的业务核心增值上。这是您将最好的开发人员、架构师和资金投入的地方。这个核心之外的任何东西都将获得更少的资源,并且通常可以外包。
为了评估这个核心是什么以及需要什么,你需要你的技术团队经常与领域专家交谈。使用一些通用语言。
编码方面本身是敏捷的,因为通常一开始你的技术团队和你的专家之间会有沟通不畅,给你一个不太好的结果。更多的交流、更多的知识共享和理解会让你对你的产品和它的架构有不同的看法:那是你重构的时候。
 
DDD 的大部分内容都是常识,它提供的是一种明确的常识。在我看来,重要的因素是明确确保开发人员和利益相关者都拥有映射到相关领域的业务流程的共享心理模型,他们都同意哪些“行话”术语映射到哪些部分该模型以及该模型(具有商定的条款)在代码中明确表示。
如果您真正了解彼此,与业务领域人员保持密切联系是最有用的。我经常看到开发人员和业务人员在讨论问题时使用同一个“每天”这个词,直到很久以后他们才发现他们对这个词的明显理解大不相同。
 
DDD 是一个绝妙的想法,我认为应该更广泛地应用:对问题域的深刻理解,在技术和产品之间共享,可以大大提高质量,减少错误,简化沟通,甚至产生更好的结构化代码。特别是,它往往会导致代码结构遵循底层问题域的结构,这往往比代码更清晰,代码只是试图实现功能而没有对其目的的潜在理解。
另一方面,传统上与​​ DDD 相关的设计模式和实际实现非常糟糕,并且往往会导致企业软件级别的笨拙和/或对 OOP 的过度教条使用。
 

oscarcp 
我们在我现在工作的公司使用 DDD,老实说,我非常讨厌它,有时甚至让我怀疑我是否还想继续在编程领域工作(在它工作了 20 年)。
不要误会我的意思,DDD 是有意义和目的的,但有些公司将其作为获得的徽章来应用,而不是思考这个问题,你真的需要在 DDD 之后重写所有内容吗?
在我们的例子中,在“常规编程”中可能需要几个 200 行文件的简单 CRUD API 在 DDD 中变成了难以管理的噩梦,需要你至少花几天的时间进行真正深入的调查才能理解,因为它被分成了更多这 25 个文件最多包含 3 或 4 行代码,抽象层太多,我们中的最优秀的人不可能一口气完成。
现在,您可以提出“您做错了(tm)”的论点,但由于我只是这个特定计划中的无人机并且没有任何回旋余地(团队在这方面非常不灵活)我必须遵循它信。
  
crdrost 
与业务沟通实际上是 DDD 的核心信息。它可能应该被称为“人类学设计”,因为“领域专家”是您软件的非技术用户的同义词(领域是业务领域,这是您的软件试图为他们做的任何事情)。信息是你必须在他们的自然栖息地观察你的用户。
我不明白为什么人们发现在 DDD 中很难绘制上下文边界。所以有界上下文是一种编程思想,在编程中我们称之为命名空间,它们的存在是为了消除两个相同的名称之间的歧义。DDD 说我们需要这样做,因为业务的不同部分将使用相同的词来指代不同的事物,而试图让业务的任何一方使用不同的词是容易出错和失败的主张。所以我们需要命名空间,这样我们的领域专家都可以用他们自己的语言说话,我们可以理解他们,因为在这种情况下我们使用这个命名空间,在那种情况下我们使用那个命名空间。所以:你在哪里画边界?换句话说,你的模块应该有多大?(或者现在,“模块”改为“微服务”。)
很简单:根据用户在与系统交互时似乎关心的事物种类以及他们谈论世界的不同方式,将用户划分为多个组。有界上下文不是“实体”或“强实体”或服务发现阈值,而是一种人类学构造,就像 DDD 中的其他一切一样。“运输部门的人出于一个原因关心这一点,账单部门的人出于另一个原因关心它,他们通常不会互相交谈,但我想有时他们会......”听起来像你有一个运输模块/微服务和计费模块/微服务。边界是人的边界。
 
orthoxerox
DDD 技术模式是一堆奇怪的东西。
一方面,那里有很多聪明的概念。例如,每个人都应该知道聚合是什么。
另一方面,它们中的许多与 DDD 的基本前提几乎没有关系。您可以在没有这些特定模式的情况下使用 DDD。
另一方面,人们开始在任何地方应用每一种模式,就像他们对 GoF 模式所做的那样。
我建议刚接触 DDD 的人首先参与了解战略设计部分。使用它的基本原理、优点和缺点等。在您了解 DDD 的作用与您想要实现的目标之前,请远离战术模式。战略与战术是完全不同的关注点。大量 DDD 信息的问题在于,人们倾向于采用战术方式尽早引入各种不需要的架构。请注意,战略分析的结果很可能会得出结论,简单的 CRUD 设计是最好的前进方式(对于子域甚至整个项目)。非技术领域的理解是最重要的,并且还有助于在整个开发过程中保持(非技术)利益相关者的参与。
 
对我来说,重要的是战略模式,即你如何思考谈论你的领域。许多(微服务)软件出错的是有界上下文和 API,可以通过事件风暴(讨论您的域中发生什么样的事情)和上下文映射(该 [子] 域如何与其他域交互)来改进它们。然后是无处不在的语言,或者说它对业务来说是什么,
我是一名阿根廷西班牙语-英语双语程序员。我尝试只用英语编程,但在开发与阿根廷系统的集成时,我经常不得不退回到西班牙语代码。
一般来说,最好只使用英文,因为代码更易读(因为语言的关键字和 API 都是英文的),而且要保持与名词-形容词顺序、动词时态等的一致性。一些本地概念可以很容易地翻译(或者至少看起来是这样)就像invoice-factura ......但是我不可避免地会得出没有明显翻译的概念(所以我可以弥补,但对其他人来说不够清楚)或者对于其中翻译并不完全一致。
 
pydry
我注意到类似的东西。在我们的代码库中引入 DDD 的最小 PR 使代码库膨胀了大约 1,000 行,散布在整个代码库中。我认为如果我们在任何地方都使用它,它最终总共会增加大约 12-15,000。那本来是虫子的肥沃温床。
这些想法对需要频繁重构的逻辑复杂代码有意义,但在大多数情况下,所有不同层之间的严格分离只会导致大量代码。太多了。
我们也无法就有界上下文的限制真正在哪里达成一致。在这个问题上的大多数文档是一个单纯的handwave说:“你看着办吧”或者“当你做这些练习与企业,它会变得清晰”(它没有),这是奇怪给出如何极其重要的是,如何将其损坏是绑定错误的东西。
无法就有界上下文的限制真正在哪里达成一致。这是软件设计中最难的部分。难怪没有明确的规则来说明如何做到这一点。您必须既是领域专家又是实施专家,才能在第一次尝试时确定边界。
我也不太相信这是您可以通过更好地与“业务”沟通来获得的东西。
这在一定程度上激怒了我,因为遵循 DDD 模式需要大量的代码 - 如果边界错误,代码量的 3-4 倍意味着重写成本的 3-4 倍。
 
bob1029
又一个超载的技术流行语。对我来说,DDD 意味着两件事:
1. 倾听您的客户如何评价他们的业务并将这种观点内化,就好像您的生活依赖于它一样(您的职业当然可能)。
2. 找到一种方法来构建您的产品并以类似的方式进行交流。该方法应被理解为特定领域的
任何将哲学扩展到这些点之外的东西都是对您的产品做出假设,而这些假设实际上只有您和您的客户应该做出。
我在这里做出的唯一具体的技术假设是,如果您要处理大量不同的类型和来自业务的复杂、快速变化的需求,您可能至少需要考虑 SQL 几分钟。如果你能找到一种方法让企业用他们理解的语言编写他们自己的逻辑/查询,那么你就发现了我个人目前所知道的最强大的力量倍增器。
 
sovietmudkipz
我正在努力研究如何将 DDD 应用于视频游戏上下文,特别是使用 unity3d。这是一场斗争,因为我认为游戏创造者的文化时代精神不喜欢应用来自“外部”影响的软件模式,比如企业软件。
我认为游戏软件是整个系统,而不仅仅是游戏客户端。这意味着任何后端或基础设施都是复杂整体的一部分。
后端系统的 DDD 很好理解。不同之处在于它需要为游戏客户端以及任何其他客户端提供服务(例如营销游戏的网站、发布游戏相关的论坛,甚至可能运行聊天机器人的逻辑)。如果您想向您的游戏客户端公开在线服务,后端系统是您需要考虑要运行的逻辑和要持久化的状态的地方。
我喜欢 DDD 用于某些类别的游戏客户端代码,而不是全部。有一些简单的游戏元素是由游戏引擎物理和碰撞驱动的。有更复杂的游戏元素可以从更复杂的域建模技术中受益。基本上允许自己为简单的游戏元素编写未经测试/轻微测试的组件。对于复杂的系统,将统一性视为领域特定逻辑的具体实现可能是有益的。用游戏系统专家的语言对代码进行建模,并考虑如何将统一运行时事件(用户输入、碰撞)转换为对该领域有意义的术语。
实际上,统一游戏对象代表领域实体,游戏对象之间的交互可以触发领域逻辑。例如,与战斗游戏对象碰撞的弹丸游戏对象是执行战斗服务逻辑的好地方,这可能会触发域事件(可观察行为),然后订阅者可以监听这些事件(例如损坏 UI、吐司通知、或任何其他统一用户界面)。
 
revskill 
我认为领域驱动设计的诞生是为了与数据库驱动设计区分开来。所以我们在更高的层次上设计,我们的思维过程,词汇与商业术语联系在一起。
我们过去常常使用 UML 设计更高级别的系统:用例图是一种设计工具,用于与产品所有者讨论领域的主题和动词。序列图使我们能够捕获域级别的任何动态和交互。
 
piaste
在未经验证的数据和经过验证的数据之间有一个清晰的、强类型的分离。
未经验证的数据(DTO)只是输入、从存储读取、从外部服务接收等的原始表示。任何可能的输入都应该是可接受的,并且可以忠实地表示为 DTO。
然后,通过将 DTO 传递给验证函数,您可以返回一个经过验证的对象(模型),该对象实际上被限制为仅包含合法数据状态;或一组可以采取行动的验证错误。
你的业务逻辑应该只在经过验证的对象上运行,这样你才能真正依赖你的基本假设,并且可以表达实际的工作流规则(例如“你不能结帐一个空的购物车”)并与琐碎的验证(例如。 “数量必须大于 0”)。
 
DDD 是一个软件工程概念,如果你有100 名十年经验的软件工程师,那么他们中的 30 人会不知道或理解得很差,然后你会有 70 种不同的解释。