从层到功能:探索 .NET 中的垂直切片体系结构

科技   2024-11-07 05:03   上海  


我们构建软件的方式不断发展。虽然 Clean Architecture 一直是 .NET 开发人员的最爱,但 Vertical Slice Architecture 以其以功能为中心的方法而受到关注。那么,让我们快速深入了解 Vertical Slice,将其与 Clean Architecture 进行比较,看看它是否适合您的下一个项目!

But,Vertical Slice 会是答案吗?或。传统架构可能很僵化且难以扩展,有时很难使代码与业务逻辑保持一致。Vertical Slice 按功能组织所有内容,为您提供更大的灵活性。但让我们说实话——我们不知道它是否适合所有情况。这一切都是为了尝试一下,看看它如何满足您的需求。准备好探索了吗?让我们开始吧!

垂直切片架构,其中每个功能(例如,“创建书籍”和“创建用户”)都通过不同的层移动:UI、域、存储库和数据库。每个切片都是自包含的,处理跨所有层的特定流,确保模块化和功能隔离。

理解 Vertical Slice 的理想方法之一是将其与 Clean Architecture 等传统的分层架构进行比较。让我们看看他们是如何叠加的!

Clean Architecture (Left Side):

  • 像蛋糕一样分层:Clean Architecture 将应用程序分成多个层,就像蛋糕一样。你有自己的控制器 (UI) 和业务逻辑。一切都被整齐地分成自己的层。Api.csprojApplication.csproj

  • 命令和功能:在 Clean Architecture 中,您的命令 like 和 隐藏在 Application 下它们自己的文件夹中。但这里有一个问题 — 它们被分成多个图层,所以当您想创建一个功能时,您必须在每个图层中烘焙一点!CreateBookHandler.csCreateUserHandler.cs

Vertical Slice Architecture (Right Side):

  • 一个功能,一个切片:现在想象一下,您不是像千层蛋糕一样把东西分开,而是抓住整个功能并将其包装成一个整齐的包装!这就是 Vertical Slice。每个功能(如 “Books” 或 “Users”) 都有自己的小捆绑包,其中包含所需的一切。

  • All-In-One:您的 “Books” 功能包含其控制器、数据模型、命令,甚至逻辑 (, , )。无需在图层之间跳转 - 一切都在一个切片中。这就像把你最喜欢的菜的所有配料都放在一个碗里一样!Book.csCreateBookCommand.csBooksController.cs

So, What’s the Big Difference?

  • 当您需要整齐、分层的职责分离时,Clean Architecture 非常棒。但是,当您需要跨层跳转以完成单个功能时,可能会感觉有点慢。

  • 另一方面,Vertical Slice 就像快速通道!它是功能优先的,这意味着功能所需的一切都捆绑在一个地方。易于查找,易于更改。

隔离😡的挑战:功能隔离出错

好了,我们被 Vertical Slice 的想法所吸引 — 它速度快,是模块化的,并且它把所有东西都放在漂亮的小捆绑包中。但这里有一个问题:当你如此完全地隔离特征时,它可能是一把双刃剑

想象一下,你有自己的整洁切片:一个用于 Books,另一个用于 Users,还有更多用于其他功能。一切都运行正常,直到有一天您需要访问 Books 切片中的 User 数据 — 也许是为了检查用户是否可以借书。呃哦!由于这些切片是独立的,因此您无法直接从 Books 切片访问 。这就是事情变得棘手的地方。UserRepository

该图像描述了一个垂直切片架构,其中 “Books” 功能直接从 “Users” 功能访问。这打破了特征隔离的原则,因为垂直切片架构中的每个特征都应该是自包含独立的。跨功能直接共享存储库会形成紧密耦合,使系统更难维护。相反,应该通过共享服务共享内核来处理共享逻辑(如获取用户),以保持功能隔离和模块化。ReserveBookQuery.csUserRepository.GetUsers()

解决方案:保持切片的整洁但相互连接

我们希望保持 slice 的独立性,但有时 slice 需要相互通信。以下是在不使代码库变得一团糟的情况下处理此问题的方法:

Shared Kernel 来救援 🛟

Shared Kernel 是一个很酷的中间人,它允许 slice 共享逻辑、数据和事件,而不会互相踩踏。方法如下:

1. Shared Services

切片可以调用共享服务(如 a )来获取公共数据或逻辑,而无需在功能之间创建依赖关系。UserService

切片(“Books”和“Users”)都通过 UserService 或类似服务访问共享逻辑或数据,这些服务位于提供通用功能的共享内核中。

示例

“Books” 切片用于在有人借书时获取用户信息。UserService

2. 事件驱动的通信

切片发布和订阅事件类似于在整个应用程序中触发操作,同时保持松散耦合。BookBorrowedEvent

该图显示了在运行 后在共享内核中触发 的 书籍功能。Users 功能侦听此事件并更新 中的用户历史记录。该活动就像一个信使,让两个功能在不直接连接的情况下进行对话,保持一切模块化和简洁!BookBorrowedEvent.csBorrowBookCommand.csUpdateUserHistory.cs

3. 暴露的内部 API 端点

切片公开其他切片调用以交换数据的内部 API 端点,例如应用程序内的微型微服务。

示例

“Books” 切片向**“Users”**切片发送 HTTP 请求,以通过 获取用户详细信息。/api/users/{id}

现在我们已经探讨了切片如何通信,重要的是要记住并非所有模式都适合每个场景。每个项目、功能和用例都是独一无二的,因此在确定切片应如何通信时,请注意并仔细评估

  • 对于紧密耦合、可重用的逻辑共享内核中的共享服务可能非常适合。

  • 对于松散耦合和对变化做出反应:事件驱动的通信使事情保持灵活。

  • 对于结构化的切片间数据交换:内部 API 端点是您的迷你微服务解决方案。

选择错误的通信方式可能会造成混乱,所以问问自己:我的切片需要有多独立?我的使用案例是否需要立即响应,还是可以异步响应事件?让系统的需求指导您的决定。

🚀 开发人员的过渡:从干净的架构到垂直切片

从 Clean Architecture 转向 Vertical Slice 不仅仅是代码结构的转变,而是您对功能开发和团队协作的看法的转变。以下是预期内容:

1. 更快的功能开发

  • 减少跳转:将您需要的所有内容(命令、查询、处理程序)都放在一个切片中,从而简化了添加新功能的过程。不再需要跨图层或多个项目导航。

  • 即时聚焦:开发人员只关注该功能,减少上下文切换并允许更快的迭代。

2. 减少合并冲突

  • 隔离的功能切片:由于切片是自包含的,因此接触相同文件的开发人员较少,从而减少了大型团队中发生合并冲突的机会。

  • 独立的工作流程:团队可以在单独的切片上工作,而不会互相踩踏,从而提高整体生产力。

3. 提高团队生产力

  • 功能优先的思维方式:开发人员可以端到端地交付功能,而不会因依赖关系而减慢其速度。无需等待其他团队更新服务或存储库。

  • 并行开发:由于切片独立性,团队可以同时处理多个功能,从而加快交付周期。

4. 更清晰的所有权

  • 自包含切片:团队完全拥有自己的功能,从表示到数据库逻辑。这使得责任更加明确,并避免在出现 bug 时推卸责任。

  • 更轻松的入职培训:新团队成员可以专注于单个功能切片来了解系统,而不是浏览多个层次。

5. Risk of Ball of Mud

  • 过度填充 slice 的诱惑:注意在一个 slice 中添加太多 logic 或直接从另一个 slice 调用方法。这可能会导致紧密耦合的混乱,从而抵消模块化的好处。

  • 解决方案:使用共享内核事件驱动型通信或 API 端点负责任地处理跨切片逻辑。

6. 代码复制与重用

  • 受控重复:您可能会遇到需要在多个切片中出现相同逻辑的情况。虽然一些重复是可以接受的,但请避免过度依赖共享服务以保持切片独立性。

  • 解决方案:在绝对必要时通过 Shared Services 重用通用逻辑,但要遵守功能之间的界限。

7. 沟通复杂性

  • 没有自由的方法调用:与可以跨层共享服务和存储库的 Clean Architecture 不同,Vertical Slice 需要一种更周到的方法来处理切片之间的通信。

  • 解决方案:仔细评估何时使用 EventsShared Services 或 API 进行切片间通信,以避免功能紧密耦合。

最后的思考

Vertical Slice 和 Clean Architecture 都有其独特的优势和权衡。Vertical Slice 在功能开发速度方面确实大放异彩。由于每个功能都是独立的,因此无需浏览多个图层 — 您需要的一切都在一个地方。这种简单性使开发人员能够快速进入和退出,从而减少摩擦并实现快速迭代。但是,当 slice 需要相互通信时,这种模块化需要仔细管理以避免引入复杂性。优点是它通过保持功能隔离和可管理来帮助防止大泥球

相比之下,Clean Architecture 在模块重用关注点分离方面提供了灵活性。虽然跨层轻松调用方法很诱人,但如果没有规则这样做可能会导致技术债务紧密耦合的系统,从长远来看,这会使维护变得更加困难。但是,当与域驱动设计 (DDD) 原则结合使用时,Clean Architecture 可以有效地在层之间实施明确的边界,使核心域与外部关注点解耦,并确保可扩展、可维护的结构。

Clean Architecture 的一个关键点是其层重用的潜力。例如,持久性层域层通常可以通过利用类库跨平台重用,例如 Web 和移动应用程序。这在 Vertical Slice 中更难实现,因为每个 Slice 都与特定特征紧密绑定。

没有一个完美的条件可以证明 Vertical Slice 或 Clean Architecture 绝对是最好的。这完全取决于您的项目需求团队的工作流程和您的长期目标。但是,通过正确的用例团队结构和开发优先级,Vertical Slice 可以提供一种非常有效的方法来构建快速、隔离和模块化的系统。同样,当可重用性和关注点分离是项目优先事项的最前沿时,Clean Architecture 也会表现出色。

如果你喜欢我的文章,请给我一个赞!谢谢


架构师老卢
资深软件架构师, 分享编程、软件设计经验, 教授前沿技术, 分享技术资源(每天发布电子书),每天进步一点点...
 最新文章