Conceptual Contours

Conceptual Contours / 概念轮廓

Domain Excavation Because it requires constant discovery and refinement, building software is much like excavating a buried house. One requirement exposes a section of wall. A bug fix reveals a doorway we hadn’t accounted for. A conversation with a user turns up a hearth or a pottery shard. Gradually, the pieces begin to suggest architectural features yet to be discovered: rooms, connections, and the activities they supported. Recognizing the architectural whole changes how we understand the pieces. A wall has a purpose in relation to the rooms it separates; a doorway tells us how those rooms connect. Each discovery helps us interpret what we’ve already found and gives us a better idea of where to look next. Of course, none of this happens if we’re not looking for the structure to begin with, via a “house-seeking mind”.

领域挖掘 由于软件构建需要不断的探索与精炼,它非常像是在挖掘一座被掩埋的房屋。一个需求可能会暴露出一段墙壁;一个 Bug 的修复可能会揭示出一个我们未曾预料到的门口;与用户的一次交谈可能会发现一个壁炉或一片陶器碎片。渐渐地,这些碎片开始暗示出尚未被发现的建筑特征:房间、连接处以及它们所支持的活动。识别出建筑的整体结构会改变我们对各个部分的理解。墙壁的存在是为了分隔房间,而门口则告诉我们房间是如何连接的。每一次发现都有助于我们解读已有的发现,并让我们更清楚下一步该去哪里挖掘。当然,如果我们一开始就没有抱着“寻找房屋”的心态去寻找结构,这一切都不会发生。

After several years of daily software development and maintenance, I started getting the sense that there was something there beneath all the switch statements, loops, and if/else branches. A concept, a process, sometimes a physical object that my code was outlining (and often mangling beyond recognition). More importantly, I suspected that if I could identify that thing and align the code with it, subsequent requirements and fixes would be easier to accommodate. I was certain that each change and fix had a natural “home”, a place where it conceptually belonged. As Eric Evans puts it in the “Conceptual Contours” section of The Big Blue Book, “there is a logical consistency deep in most domains, or else they would not be viable in their own sphere”.

在经历了数年的日常软件开发与维护后,我开始感觉到在所有的 switch 语句、循环和 if/else 分支之下,隐藏着某种东西。那是一个概念、一个流程,有时是一个物理对象,我的代码正在勾勒(却往往将其扭曲得面目全非)着它。更重要的是,我怀疑如果我能识别出那个东西并使代码与之对齐,后续的需求和修复将更容易处理。我确信每一个变更和修复都有一个自然的“家”,一个它在概念上所属的地方。正如 Eric Evans 在《领域驱动设计》(大蓝书)的“概念轮廓”一节中所言:“大多数领域深处都存在逻辑一致性,否则它们在各自的领域内将无法生存。”

Intuiting Contours Most applications need to store and retrieve data, handle concurrency, and report failures. StubHub, Netflix, Spotify and NASDAQ all have to read and write data across services and databases, validate input, and manage concurrency. However, none of them have to worry about how to split a restaurant’s tip pool, calculate the cost of parking when a car leaves the lot, or how many seats to sell beyond a flight’s capacity. Very often, the complexities of the business the application models are what make it uniquely challenging. Best practices for EF Core, REST semantics, or tracing a request across physically disparate microservices can be googled. Design guidance for a specific customer’s seasonal pricing, handling, and shipping rates can’t be. The “something there” I sensed beneath the loops and branches was the model, the inherent structure of the business, bursting at the seams. DDD helped me bring it into focus—and give subsequent changes somewhere meaningful to belong.

直觉感知轮廓 大多数应用程序都需要存储和检索数据、处理并发并报告故障。StubHub、Netflix、Spotify 和纳斯达克都需要在服务和数据库之间读写数据、验证输入并管理并发。然而,它们都不需要担心如何分配餐厅的小费池、如何计算车辆离开停车场时的费用,或者在航班满员后还要超售多少座位。通常情况下,应用程序所建模的业务复杂性才是其独特挑战的来源。关于 EF Core、REST 语义或跨物理分散微服务追踪请求的最佳实践,可以通过谷歌搜索找到。但针对特定客户的季节性定价、处理费和运费的设计指导却无法通过搜索获得。我在循环和分支下感知到的那个“东西”,正是模型,是业务内在的结构,它正呼之欲出。DDD 帮助我将其聚焦,并为后续的变更提供了一个有意义的归宿。

As programmers we’re primed to focus first on technical matters. However, when technical concerns dominate the conversation, it’s easy to make progress on the mechanics while leaving the business problem poorly understood. The code can become technically elaborate while the rules it exists to implement remain obscure. As Evans puts it, to skillfully design a solution to a business problem, we must “temper [our] technical thinking by frequently touching base with [our] intuition for the domain”.

作为程序员,我们习惯于首先关注技术问题。然而,当技术考量主导了对话时,我们很容易在机制上取得进展,却对业务问题理解不足。代码可能在技术上变得非常复杂,而它所要实现的规则却依然模糊不清。正如 Evans 所言,为了巧妙地设计出业务问题的解决方案,我们必须“通过频繁地回归对领域的直觉,来调和我们的技术思维”。

Aligning code with the contours of the business also makes it easier to think about. So much of the mental effort in development goes into translating between what the business does and how the software represents it. We encounter grouping, sorting, counting, and arithmetic, expressed through inheritance, lambdas, and closures, and have to reconstruct the business meaning before we can even consider whether it’s correct or how to modify it. A promotion like “buy two, get one free” can disappear into those mechanics. When the code explicitly models that promotion and its rules, we can hold it in mind as a recognizable business concept. Less of our attention goes into figuring out what the code means, leaving more available to reason about what it should do.

将代码与业务轮廓对齐也让思考变得更容易。开发中大量的脑力劳动都花在了“业务实际操作”与“软件如何表示它”之间的转换上。我们遇到通过继承、Lambda 和闭包表达的分组、排序、计数和算术运算,必须先重构出业务含义,才能考虑它是否正确或如何修改。像“买二送一”这样的促销活动可能会消失在这些技术细节中。当代码显式地建模了该促销及其规则时,我们就能将其作为一个可识别的业务概念记在脑海中。我们花在弄懂代码含义上的精力减少了,从而有更多精力去思考它应该做什么。

Recognizing the Contours Technical refactoring can help us express that understanding, but extracting methods or splitting classes doesn’t necessarily produce it. We can make the code tidier while leaving the business concept just as obscure. How do we recognize those contours as we uncover them? One guide Evans offers is to ask which distinctions matter to the people the software serves. In his paint-mixing example, users combine whole paints. They don’t need to manipulate the individual pigments. A model for paint chemists might need those distinctions, but introducing them into the mixing application would add detail without helping express the work its users perform. The domain gives us a basis for deciding what to keep together and what to separate.

识别轮廓 技术重构可以帮助我们表达这种理解,但提取方法或拆分类并不一定能产生这种理解。我们可以让代码变得更整洁,同时让业务概念依然模糊。当我们揭示这些轮廓时,该如何识别它们呢?Evans 提供的一个指南是:询问哪些区别对软件服务的用户来说是重要的。在他的油漆混合示例中,用户混合的是成品油漆,他们不需要操作单个颜料。针对油漆化学家的模型可能需要这些区别,但将其引入混合应用程序只会增加细节,而无助于表达用户所执行的工作。领域为我们决定“什么该放在一起,什么该分开”提供了基础。

Our understanding of those boundaries develops as the software changes. A new requirement may reveal that two rules we grouped together actually vary independently. Another may expose the same business concept implemented in several places. These changes provide evidence about the shape of the model. Evans’s “shearing patterns” give us something to observe: where the design pulls apart as our understanding grows. That connects back to the excavation. Each discovery can change our interpretation of what we’ve already uncovered. Recognizing a doorway changes our understanding of a wall; recognizing a business policy can change how we organize several apparently unrelated conditions, or better yet, anticipate rules and conditions that have yet to be uncovered. The “house seeking mind” matters throughout. As requirements, conversations, and bug fixes expose more of the business, we have to keep asking how those discoveries fit together—and let the code reflect what we learn.

我们对这些边界的理解随着软件的变更而发展。一个新的需求可能会揭示出我们原本归为一组的两个规则实际上是独立变化的。另一个需求可能会暴露出同一个业务概念在多个地方被重复实现。这些变化为模型的形状提供了证据。Evans 的“剪切模式”(shearing patterns)为我们提供了一个观察点:随着我们理解的加深,设计在何处发生断裂。这又回到了挖掘的比喻。每一次发现都可能改变我们对已挖掘部分的解读。识别出一个门口会改变我们对墙壁的理解;识别出一项业务政策可以改变我们组织若干看似无关条件的方式,或者更好的是,预见到尚未被发现的规则和条件。这种“寻找房屋的心态”贯穿始终。随着需求、对话和 Bug 修复揭示出更多的业务细节,我们必须不断追问这些发现是如何组合在一起的,并让代码反映出我们的所学。