Fractal Architecture, Cognitive Load, Vertical Slices and other terms that do(n't) fit your head分形架构、认知负荷、垂直切片以及其他那些让你(不)理解的术语
I wrote on LinkedIn that it seems that there are never enough articles on Vertical Slices. Each time I write a follow-up, there’s another follow-up needed. And guess what? After last week, I need another one!我在 LinkedIn 上写道,关于垂直切片的文章似乎永远都不够。每次我写后续文章,总还需要再写一篇。你猜怎么着?上周之后,我又得再写一篇了!
Adam asked me in the comments:Adam 在评论区问我:
How do you see Fractal Architecture fitting into this approach? To me, it makes sense to place shared code in the smallest logical grouping of slices that use it, keeping it close to its usage while isolated from the rest of the application.你认为分形架构如何融入这种方法?对我来说,将共享代码放置在调用它的切片的最小逻辑分组中是有意义的,这样既能使其靠近使用位置,又能将其与应用程序的其他部分隔离开来。
And to give you a proper answer, I need to start by telling you about the Aussie guy called John Sweller.为了给你一个恰当的回答,我需要先跟你讲讲一位名叫 John Sweller 的澳洲人。
In 1988, he coined an important term: Cognitive Load. He said:1988 年,他提出了一个重要的术语:认知负荷。他说:
“Our working memory is only able to hold a small amount of information at any one time and that instructional methods should avoid overloading it in order to maximise learning”“我们的工作记忆一次只能容纳少量信息,教学方法应避免使其过载,以最大限度地提高学习效率。”
When I was younger, I believed that I could multitask; I also bragged that I had such a good memory that when I started managing my calendar and taking notes, I got old. And yeah, it seems that I’m old now.我年轻时曾相信自己可以多任务处理;我还吹嘘自己记忆力超群,以至于当我开始管理日程和做笔记时,我就变老了。没错,看来我现在确实老了。
When our industry evolved from the machines responsible for calculations and highly specialised machines and their operators, and became a big, valuable industry, we started to have issues with scaling. Not only technical scaling of distributed systems, but most importantly, scaling the work delivery.当我们的行业从负责计算的机器和高度专业化的机器及其操作员演变而来,并成为一个庞大且有价值的产业时,我们开始遇到扩展问题。这不仅是分布式系统的技术扩展,更重要的是,工作交付的扩展。
Our teams grew bigger, and more and more industries started to benefit from the automation that computers brought. Developers slowly stopped working in a narrow vertical structure with business folks, but became jacks of all trades and masters of none. Or actually, some had to become masters of the one: technology.我们的团队规模不断扩大,越来越多的行业开始受益于计算机带来的自动化。开发人员逐渐不再与业务人员在狭窄的垂直结构中工作,而是成为了样样通样样松的“万金油”。或者说,有些人不得不成为某一领域的专家:技术。
The pendulum swung, and we landed in the Helios Technology-centric world:钟摆摆动,我们落入了以技术为中心的 Helios 世界:
And yes, I know, in theory, the entities are “Enterprise Business Rules”, which means that business logic should be at the heart of our application, but effectively what you see, and where the emphasis is, are the layers. Even if you measure the surface of those layers, those technical layers are big enough that we can lose sight of what we are here for. And we’re here to deliver business value through software.是的,我知道,理论上这些实体是“企业业务规则”,这意味着业务逻辑应该处于应用程序的核心,但实际上你所看到的以及重点所在,都是层。即使你测量这些层的表面积,这些技术层也大到足以让我们忘记我们为何而存在。而我们在这里是为了通过软件交付业务价值。
Of course, for organisations it was easier to tell:当然,对于组织来说,这样说更容易:
-
hey, you don’t need to care what you do, just deliver this nicely looking UI,嘿,你不需要关心你在做什么,只要交付这个好看的 UI 就行,
-
just take care of those distributed services integration, ensure they work, and you don’t need to care what they do,只要负责那些分布式服务的集成,确保它们能工作,你不需要关心它们是做什么的,
-
hey, you’re the chosen one, smarty pants, right? Yeah, you’ll work on the domain.嘿,你是天选之子,聪明人,对吧?是的,你负责处理领域逻辑。
It was justifiable to some degree, since all of that required specific technical skills that took time to master. It was also easier to just throw people who were supposed to be “good enough” to solve cases. Learning domains takes time. For me, it was usually about a year before I felt comfortable enough to understand the business. So people hoped that breaking down the work by technical aspects would cut the onboarding time.这在一定程度上是合理的,因为所有这些都需要特定的技术技能,需要时间来掌握。把那些被认为“足够好”的人扔去解决问题也更容易。学习领域知识需要时间。对我来说,通常需要一年左右的时间,我才能感到足够舒适去理解业务。所以人们希望通过技术层面来拆解工作,能够缩短入职时间。
But well, it ended up as something explained by a similar diagram:但结果却像类似的图表所解释的那样:
Yup, collapsed like Atlantis,没错,像亚特兰蒂斯一样崩塌了,
Why did this fail? I wrote about it in:为什么会失败?我曾在以下文章中写过:
-
What does Mr Bean opening the car have to do with programming?憨豆先生开车与编程有什么关系?
-
What onion has to do with Clean Code?洋葱与整洁代码(Clean Code)有什么关系?
In short? It skyrocketed the cognitive load. Our architecture should be optimised to keep together things that change together. That way, we can work within a certain scope without jumping from one place to another. And that’s what working with layered architecture looks like.简而言之?它让认知负荷飙升。我们的架构应该优化为将一起变化的事物保持在一起。这样,我们就可以在一定的范围内工作,而无需从一个地方跳到另一个地方。而这正是使用分层架构的样子。
To add one web api endpoint, you need to change presentation, application, domain, and storage layers, each kept entirely separately just for the sake of subjective cleanliness. Also, if you had to understand what you have to do to complete a feature that touches multiple other features, you need to have a PhD.要增加一个 Web API 端点,你需要修改表示层、应用层、领域层和存储层,每一层都为了所谓的主观整洁而完全分开。此外,如果你必须理解为了完成一个触及其他多个功能的特性需要做什么,那你得有博士学位才行。
In general, the investigations looked like wanting to check the weather on your phone, and then realising 2h later that you just bought a skateboard after going through the path of reminiscences brought by doom scrolling on social media. And you still don’t know if it’s going to rain or not.总的来说,调查过程看起来就像是想在手机上查看天气,结果两小时后发现自己在社交媒体上疯狂刷屏,陷入回忆路径后买了个滑板。而你依然不知道会不会下雨。
So the pendulum swung again. We like to bounce back and forth; just like a pendulum, we can’t stop somewhere in the middle. Many people are now claiming that we don’t need any layers, that we should be vertically aligned. So we should cut our applications down to only the business features. We don’t need any grouping; what we care about is being autonomous and delivering features separately.于是钟摆再次摆动。我们喜欢来回摇摆;就像钟摆一样,我们无法停在中间的某个位置。许多人现在声称我们不需要任何层,我们应该垂直对齐。因此,我们应该将应用程序拆解为仅包含业务特性。我们不需要任何分组;我们关心的是自治,并分别交付特性。
The initial idea was nice, something like “let’s get back to the roots and think differently, keeping the focus on business”. Yet, we got semantic diffusion of the original idea, and what was heard the most was: no layers, just vertical features.最初的想法很好,类似于“让我们回归本源,换个角度思考,保持对业务的关注”。然而,我们得到了原始想法的语义扩散,听到最多的是:没有层,只有垂直特性。
I explained that in detail in My thoughts on Vertical Slices, CQRS, Semantic Diffusion and other fancy words.我在《关于垂直切片、CQRS、语义扩散和其他花哨词汇的思考》一文中详细解释了这一点。
Why is it an issue?为什么这是一个问题?
Have you seen a huge backlog in Jira? Just a huge to-do list filled with tasks for some features.你见过 Jira 里巨大的积压任务吗?就是一个充满了各种特性任务的巨大待办事项列表。
How easy is it to understand what actually has to be done?理解到底需要完成什么有多容易?
How do you manage the priority?你如何管理优先级?
How do you understand dependencies, and why are those features actually added?你如何理解依赖关系,以及为什么要添加这些特性?
Typically, that’s where the Jirology starts, and that’s why we hate it. But the issue originates somewhere else.通常,这就是“Jira 学”开始的地方,也是我们讨厌它的原因。但问题源于其他地方。
Cynically, or using Occam’s razor, the answer is simple. More or less how W. Edwards Deming framed it:愤世嫉俗地说,或者使用奥卡姆剃刀,答案很简单。大致如 W. 爱德华兹·戴明所言:
A bad system will beat a good person every time.一个糟糕的系统总是会打败一个优秀的人。
If our goal is the same: scaling the development process and its output, then no matter what we change internally, which architecture we choose, it’ll diffuse into what we optimise for.如果我们的目标是一致的:扩展开发流程及其产出,那么无论我们在内部改变什么,选择什么架构,它都会扩散到我们所优化的方向上。
So, as always, breaking the business capability into a smaller set of features was how many architects and CEOs distributed the work and made it parallel. Faster rarely means better. Unless we’re in the race car industry.所以,一如既往,将业务能力分解为更小的一组特性,是许多架构师和首席执行官分配工作并使其并行化的一种方式。更快很少意味着更好。除非我们是在赛车行业。
Also, for many software engineers, feature-based delivery was a way to simplify their lives. They could write a vague ADR, with just a few sentences describing the idea. It’s easy to write something that sounds good, but without enough detail, it’s hard to tell the implications and the exact delivery plan.此外,对于许多软件工程师来说,基于特性的交付是一种简化生活的方式。他们可以写一个模糊的 ADR(架构决策记录),只用几句话描述想法。写出听起来不错的东西很容易,但如果没有足够的细节,就很难判断其影响和确切的交付计划。
Now, when you have the vague plan accepted, you can slowly, feature by feature, deliver lower-quality, simplistic solutions, backing this up with the claim that现在,当模糊的计划被接受后,你可以慢慢地,一个特性接一个特性地交付质量较低、过于简单的解决方案,并以此作为支持:
“Well, it’s done according to the specification we agreed on. We don’t have enough time to go again through this process. We need to deliver the feature.”“嗯,这是按照我们商定的规范完成的。我们没有足够的时间再次经历这个过程。我们需要交付这个特性。”
Sounds familiar? For me, too familiar. I have seen numerous cases where requirements drift one after another, slice by slice. And teams backing their next change with the previous one.听起来很熟悉?对我来说也是如此。我见过无数次需求一个接一个地漂移,切片接切片。团队用上一个变更来支持下一个变更。
I’ve also seen numerous cases where teams felt really comfortable narrowly focusing on the exact features without even asking what the bigger picture is or why it was added. That led to, obviously, a maze, duplication of code and responsibility and lots of features that have had a hard time talking to each other.我还见过无数次团队在狭窄地关注具体特性时感到非常舒适,甚至不去问大局是什么,或者为什么要添加它。这显然导致了一个迷宫、代码和责任的重复,以及大量难以相互沟通的特性。
Ok, so should our pendulum swing again, and should we get back to what, not my uncle, was praising?好吧,那么我们的钟摆应该再次摆动吗?我们应该回到我叔叔(不是我叔叔)所称赞的东西吗?
Again, maybe the answer is to finally stop somewhere in the middle and focus on the outcome instead of the output? So, the business outcome and the software product.或许答案还是在于最终停在中间的某个位置,关注结果而不是产出?也就是业务成果和软件产品。
Indeed, we should invest in decreasing the cognitive load and making our software easier to fit in our heads. We already learned that:确实,我们应该投资于降低认知负荷,让我们的软件更容易被理解。我们已经了解到:
-
technical split is not great,技术拆分并不理想,
-
feature split is better but still far from great.特性拆分更好,但离理想还很远。
Some time ago, two people I respect a lot started talking about Fractal Architecture.前段时间,我非常尊重的两个人开始谈论分形架构。
I’m afraid to write about it, not to start counting where it becomes semantically diffused. But that’s important.我担心写关于它的文章,以免开始计算它在何处发生语义扩散。但这很重要。
Mark Seemann also wrote a great book, Code That Fits in Your Head. Check it out; it’s a simple book teaching on why code is important and design activity, and why we need to invest in fast feedback loops and simulations. Highly relevant nowadays. Much better than reading next-gen AI bros’ hot takes.Mark Seemann 也写了一本很棒的书《Code That Fits in Your Head》。去看看吧;这是一本简单的书,教导为什么代码很重要以及设计活动的重要性,以及为什么我们需要投资于快速反馈循环和模拟。在当今非常相关。比阅读下一代 AI 兄弟的热评要好得多。
Nevertheless, the idea is that, indeed, if we want our software to do what it does, to deliver business value, we should focus on the business. Yet, to avoid ending up with the overfocus and feature factory (as we discussed), we should note that we could compose it. With the idea that big things are composed of small things.尽管如此,其理念确实是,如果我们希望软件实现其目标,即交付业务价值,我们就应该关注业务。然而,为了避免最终陷入过度关注和特性工厂(正如我们讨论过的),我们应该注意到我们可以对其进行组合。秉持大事物由小事物组成的理念。
Ok, enough philosophy; let’s get back to the original example from the previous article. We discussed the following e-commerce order features:好吧,哲学谈够了;让我们回到上一篇文章中的原始示例。我们讨论了以下电子商务订单特性:
📁 orders
📁 verifying-order
📁 confirming-order
📁 registering-order
📁 pending-orders
📁 order-storage // 👈 look ma, technical concept!Now, if we added a shopping cart, then it could look like:现在,如果我们添加一个购物车,它可能看起来像这样:
📁 shopping-carts
📁 adding-product-item
📁 removing-product-item
📁 confirming-cart
📁 clearing-cart
📁 cart-storage // 👈 look ma, technical concept!We could group that into the e-commerce module我们可以将其归类为电子商务模块
📁 e-commerce
📁 shopping-carts
📁 adding-product-item
📁 removing-product-item
📁 confirming-cart
📁 clearing-cart
📁 cart-storage
📁 orders
📁 verifying-order
📁 confirming-order
📁 registering-order
📁 pending-orders
📁 order-storageIf we zip that into the e-commerce system, we’d see:如果我们将其压缩到电子商务系统中,我们会看到:
📁 e-commerce
📁 shopping-carts
📁 ordersWe could continue to elevate that into, e.g. the whole ERP system with other modules like marketing, sales, etc. Each of them would have its own sub-modules.我们可以继续将其提升,例如提升到整个 ERP 系统,包含营销、销售等其他模块。它们中的每一个都有自己的子模块。
📁 erp
📁 e-commerce
📁 marketing
📁 salesLooks familiar?看起来很熟悉?
Yes, the layout is the same as the order module. What’s more, this can also go down, so:是的,布局与订单模块相同。更重要的是,这也可以向下延伸,所以:
📁 orders
📁 verifying-order
📁 anti-fraud-detection
(...)
📁 high-value-custome-verification
📁 external-order-verification
📁 (...)It’s the same repeated pattern. Just like fractals, they’re chaotic in the sense that each fractal is different, just like each software product is different, but if we look carefully enough, then those chaotic patterns start to repeat themselves.这是重复的相同模式。就像分形一样,它们在某种意义上是混乱的,因为每个分形都不同,就像每个软件产品都不同一样,但如果我们仔细观察,这些混乱的模式就会开始重复。

So why isn’t that a standard if it’s so great and simple?那么,既然它如此伟大且简单,为什么它没有成为标准?
Things get hard when you start thinking about cross-boundary splits and their dependencies.当你开始考虑跨边界拆分及其依赖关系时,事情就变得困难了。
We already discussed with the features factory that no-boundaries is not a solution. Even if we remove them all and make all things separate, they won’t magically become autonomous. They’ll still have dependencies, just hidden, which is the worst type of coupling you can have.我们已经讨论过特性工厂,没有边界不是解决方案。即使我们移除所有边界并使所有事物独立,它们也不会奇迹般地变得自治。它们仍然会有依赖关系,只是隐藏起来了,这是你能拥有的最糟糕的耦合类型。
Let’s look again at this example让我们再看看这个例子
import { type OrderStorage } from '../order-storage;
// Dependencies
export type CheckDriver = (id: DriverId) => Promise<DriverStatus>;
export type VerifyOrderDependencies = {
storage: Pick<OrderStorage, 'getOrder' | 'saveOrder'>;
drivers: {
checkDriver: CheckDriver;
}
}
// Feature
export const verifyOrderHandler = async (
{
orderStorage: {
getOrder,
saveOrder
},
drivers: {
checkDriver
}
checkContractor,
checkDriver,
}: VerifyOrderDependencies,
command: VerifyOrder,
): Promise<void> => {
const order = await getOrder(command.orderId);
const driver = checkDriver(order.driverId),
await saveOrder(verifyOrder({ ...command, driver }, order));
};We declare explicitly what our feature needs.我们明确声明了我们的特性需要什么。
Why couldn’t we do the same for the whole module?为什么我们不能对整个模块做同样的事情?
import { verifyOrderHandler, type CheckDriver } from ‘verifying-order’;
// Dependencies
export type OrdersDependencies = {
database: {
connectionString: string
},
drivers: {
checkDriver: CheckDriver;
}
}
// Feature
export const orders = async (
props: OrdersDependencies,
command: VerifyOrder,
): Promise<void> => {
const db = drizzle(props.database.connectionString);
const storage = ordersStroage(db);
const verifyOrder = (command) => verifyOrderHandler(
{
// DEPENDENCIES
drivers: deps.drivers, // 👈 EXTERNAL
storage // 👈 INTERNAL
},
command
);
return {
verifyOrder, // 👈 EXPOSES
}
};See, we define:看,我们定义了:
-
Dependencies: what do we need from the outside world, e.g. where our data is stored (connection string) and how drivers are checked (from the outside world). Then we can feed that to child components using both external dependencies (e.g. drivers) and other internal components (e.g. storage).依赖项:我们需要外部世界的什么,例如数据存储在哪里(连接字符串)以及如何检查驱动程序(从外部世界)。然后,我们可以使用外部依赖项(例如驱动程序)和其他内部组件(例如存储)为子组件提供这些内容。
-
Capabilities: what do we expose to the outside world (e.g. order verification, which may be used by other modules, or a dedicated API component wrapping orders).能力:我们向外部世界公开什么(例如订单验证,这可能被其他模块使用,或者是一个包装订单的专用 API 组件)。
We could do the same at the higher level and the lower level. Just like Fractals, or like Hive (as nicely put by Julien Topçu & Thomas Pierrain).我们可以在更高级别和更低级别上做同样的事情。就像分形,或者像 Hive(正如 Julien Topçu 和 Thomas Pierrain 所言)。
Read also the article Onion, Hexagonal, Clean or Fractal Architecture? All of them, and more!! where Urs Enzler explains how he mixes all of that together.还可以阅读文章《洋葱、六边形、整洁还是分形架构?它们全都是,甚至更多!!》,Urs Enzler 在其中解释了他如何将所有这些混合在一起。
And that’s also what the C4 model is doing: embracing different levels. Although it has a hard limit of 4, so it’s not a true fractal, which makes sense, since we should also be cautious about setting up paths that are too deep.这也是 C4 模型所做的:拥抱不同的级别。尽管它有 4 级的硬性限制,所以它不是真正的分形,这很有意义,因为我们也应该谨慎设置太深的路径。
Of course, that doesn’t mean we need to go crazy and define all dependencies this way. If those are calls to inner module methods or some shared code, we can call it explicitly. Then we’re coupling those components together, but if it’s explicit and helps us tame complexity and make things more explicit, then why not?当然,这并不意味着我们需要疯狂地以这种方式定义所有依赖项。如果这些是对内部模块方法的调用或一些共享代码,我们可以显式地调用它们。然后我们将这些组件耦合在一起,但如果它是显式的,并且有助于我们驯服复杂性并使事情更明确,那为什么不呢?
Nevertheless, that’s why I don’t like jargon like “bounded context”, as it’s vague, but it’s presented as precise. I prefer to be “explicitly implicit” and just say that we’re composing the application from a component tree (or building a fractal).尽管如此,这就是我不喜欢“限界上下文”这类术语的原因,因为它很模糊,却被表现得很精确。我更喜欢“显式地隐式”,只需说我们正在从组件树中组合应用程序(或构建一个分形)。
Whether it’s a system, component, module, or slice, we can tackle that the same way. Of course, each will differ in responsibility, scope, etc., but as long as we have clear dependencies expressed as: here’s what we need, and a public API as: here is what you can get from us. We can freely compose our relationship.无论是系统、组件、模块还是切片,我们都可以用同样的方式来处理。当然,每个在责任、范围等方面都会有所不同,但只要我们有明确的依赖关系(表示为:这是我们需要的东西)和公共 API(表示为:这是你可以从我们这里得到的东西),我们就可以自由地组合我们的关系。
All of that allows us to keep our cognitive load at a level where what we see fits into our heads, and focus on the business features, not technicalities. This helps us fight the forces we discussed today that push us to optimise for output rather than outcome.所有这些都使我们能够将认知负荷保持在我们所见之物能装进大脑的水平,并专注于业务特性,而不是技术细节。这有助于我们对抗今天讨论的那些推动我们优化产出而非成果的力量。
Thoughts?有什么想法吗?
Cheers!干杯!
OskarOskar
p.s. Ukraine is still under brutal Russian invasion. A lot of Ukrainian people are hurt, without shelter and need help. You can help in various ways, for instance, directly helping refugees, spreading awareness, putting pressure on your local government or companies. You can also support Ukraine by donating e.g. to Red Cross, Ukraine humanitarian organisation or donate Ambulances for Ukraine.附:乌克兰仍处于残酷的俄罗斯入侵之下。许多乌克兰人民受到伤害,无家可归,需要帮助。你可以通过多种方式提供帮助,例如直接帮助难民、传播意识、向当地政府或公司施压。你也可以通过向红十字会、乌克兰人道主义组织捐款或为乌克兰捐赠救护车来支持乌克兰。