Feature-Driven Architecture:设计可扩展的应用程序

发布: (2026年2月2日 GMT+8 18:02)
10 分钟阅读
原文: Dev.to

Source: Dev.to

Introduction

在本系列的第一篇文章中,我们讨论了 Atomic Design 作为一种构建连贯、可复用且结构良好的界面的方法。

当主要挑战是 组织 UI 时,这是一种极其有效的做法。

但随着应用的增长,UI 很快不再是实际问题。

代码增多,功能倍增,需求变化比我们希望的更快。此时,会出现新的问题:

  • 应用逻辑到底应该放在哪里?
  • 如何在不破坏其他功能的情况下修改某个功能?
  • 多个人如何在同一代码库上工作而不相互踩脚?

这就是 架构可扩展性 发挥作用的地方。
而这正是仅靠 Atomic Design 所不足的地方。

可伸缩性不仅仅是性能

当我们谈论可伸缩性时,我们常常立刻想到性能、负载或基础设施。

实际上,首个可伸缩性问题是代码本身

当一个应用无法伸缩时:

  • 每个新功能都需要在多个位置进行更改
  • 逻辑分散且难以追踪
  • 应用各部分之间的依赖是隐式且脆弱的
  • 理解一次更改的上下文比更改本身更耗费成本

简而言之:当认知负担增长速度超过代码本身时,系统就无法伸缩

面向特性的架构正是为了解决这个问题而存在的。

功能到底是什么

A 功能 并不是单个界面。
它甚至不是一个孤立的函数。

功能是 对用户有价值的单元

从架构的角度来看,功能代表:

  • 明确的目标
  • 完整的行为
  • 明确定义的上下文

当用户说 “我想做 X” 时,他们通常指的是一个功能。

以功能为中心的思考会转变关注点:

  • 从技术转向 领域
  • 从技术层面转向 功能价值

正是这种转变使得可扩展性成为可能。

面向特性的架构:关键原则

核心原则很简单:

围绕特性组织代码,而不是技术层次。

每个特性成为一个 内聚的容器,其中包含实现该行为所需的一切。

这并不意味着要消除技术概念,而是 在全局层面降低它们的可见性。细节保留在特性内部,符合其语境。

主要优势

  • 特性可以独立演进
  • 上下文清晰且受限
  • 系统通过 添加 而非 纠缠 来增长

这就是架构可扩展的方式。

示例:cart 功能

为了让概念更具体,这里提供一个单一功能可能的组织方式示例:

features/
└─ cart/
   ├─ components/   # UI 元素,专属于购物车
   ├─ helpers/      # 可复用的函数或逻辑帮助器,专属于购物车
   ├─ state/        # 购物车的状态管理(本地或全局)
   ├─ views/        # 与购物车相关的页面或屏幕
   ├─ types/        # 类型定义或接口
   ├─ tests/        # 购物车功能的测试
   ├─ mocks/        # 用于测试/开发的模拟数据或服务
   └─ index.ts      # 对外暴露功能公共部分的入口文件

示例 index.ts

// 仅导出应在功能外部使用的内容
export * from './components';
export * from './helpers';
export * from './types';

// 不要导出 state、views、tests 或 mocks
// 这些保持为功能内部实现

⚠️ 暴露特性部分的重要规则

  • 仅暴露应在特性外部使用的内容。
    组件、辅助函数和类型通常可以安全导出。

  • 其余全部保持私有。

    • state/ → 内部状态逻辑
    • views/ → 屏幕或容器
    • tests/ → 测试代码
    • mocks/ → 虚假数据
  • 始终使用 index.ts 作为对外使用的唯一入口点。
    这可以保持特性 封装,并使边界清晰。

将原子设计与特性驱动架构相结合

原子设计和特性驱动架构并不相互排斥——它们可以完美互补。

虽然特性包含业务逻辑、状态、视图和测试,原子设计提供了一套共享语言,用于构建可在多个特性之间复用的 UI 组件

文件夹结构示例

features/
├─ cart/
│  ├─ components/
│  ├─ helpers/
│  ├─ state/
│  ├─ views/
│  ├─ types/
│  ├─ tests/
│  ├─ mocks/
│  └─ index.ts
├─ checkout/
└─ wishlist/

shared/
├─ components/
│  ├─ atoms/
│  ├─ molecules/
│  └─ organisms/
└─ utils/            # shared utilities, helpers, and services

关键点

  • shared/components/atomsmoleculesorganisms → 可复用的 UI 构建块
  • features/ → 包含特性独立运行所需的全部内容

特性可以 使用共享的原子组件,同时在 components/ 中保留其特定的特性组件。

  • 这种方式保持 可复用性清晰、模块化程度高、边界定义明确,使 UI 与业务逻辑能够独立扩展。

通过将原子设计与特性驱动架构相结合,我们可以兼得两者的优势:

  • 一致且可复用的 UI
  • 可扩展且自治的特性

功能与可重用性:思维方式的转变

这种方法最有趣的影响之一是它如何改变可重用性的概念。

在特性驱动架构中:

  • 特性默认情况下不可重用
  • 可重用性是一种有意识的选择,而非最初的需求

这与我们通常对架构的思考方式,尤其是 UI 方面,截然不同。

原子化设计仍然非常有用,但其角色发生了变化:它构建 …(继续你的内容)。

通用、稳定的元素

  • 它不会在不需要的地方强迫可重用性。
  • 特性成为接受并管理复杂性的场所。
  • 共享元素变得少而坚固,真正通用。
  • 减少过早抽象,提升清晰度。

扩展团队,而不仅是代码

可扩展性的另一个常被忽视的方面是 人为因素

当代码围绕特性组织时:

  • 责任分配更容易。
  • 上下文更清晰。
  • 冲突减少。

特性成为自然的单元,用于:

  • 规划
  • 并行开发
  • 测试
  • 维护

在实践中,架构开始反映人们的 思考和工作方式
这正是系统真正可扩展的最有力迹象之一。

何时适合采用此方法

特性驱动架构并非灵丹妙药。它在以下情况下是有意义的:

  • 预期应用会增长。
  • 业务领域并非琐碎。
  • 多人协同同一代码库。
  • 变化是常态,而非例外。

在非常小的应用中,初始开销可能不值得。
但当复杂性是真实存在时,不围绕特性组织会在长期内更昂贵

结论

特性驱动架构教会我们超越单一屏幕或组件的视角:围绕独立特性组织代码可以降低冲突风险,隔离上下文,使应用更清晰、更易维护。

从这个意义上说,可扩展性不仅是技术目标——它是 代码和团队在不混乱的情况下共同成长的能力。每个特性成为可管理的生态系统,能够演进而不影响应用的其他部分。

在下一篇文章中,我们将探讨如何组合多个特性、编排它们,并使其协同工作而不失凝聚力——将一组独立单元转化为真正可扩展的前端。

Back to Blog

相关文章

阅读更多 »