Flutter 如何设计可长期维护的模块边界?


子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)

大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。

我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案,

在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。

技术方向: 前端 / 跨端 / 小程序 / 移动端工程化 内容平台: 掘金、知乎、CSDN、简书 创作特点: 实战导向、源码拆解、少空谈多落地 **文章状态:**长期稳定更新,大量原创输出

我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在"API 怎么用",而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。

子玥酱 · 前端成长记录官 ✨

👋 如果你正在做前端,或准备长期走前端这条路

📚 关注我,第一时间获取前端行业趋势与实践总结

🎁 可领取 11 类前端进阶学习资源 (工程化 / 框架 / 跨端 / 面试 / 架构)

💡 一起把技术学"明白",也用"到位"

持续写作,持续进阶。

愿我们都能在代码和生活里,走得更稳一点 🌱

文章目录

引言

很多 Flutter 项目在前期其实并不缺"结构"。甚至可以说,大多数项目一开始都有一套看起来很合理的目录:

复制代码
pages / widgets / services / models

问题不是有没有结构,而是:

这套结构,无法支撑业务持续变化。

当项目进入中后期,你会频繁遇到这些情况:

  • 一个需求要改 5 个目录
  • 一个模块改动影响多个页面
  • 想删一个功能,却发现删不干净

这些问题的本质,其实只有一个:

模块边界从一开始就没有设计好。

什么是"好的模块边界"

很多人理解模块边界,是"文件放在哪"。但真正有效的边界,应该解决三个问题:

  • 哪些代码可以改
  • 哪些代码不能随便动
  • 改动会影响到哪里

换句话说,一个好的模块边界应该具备:

可独立理解

不用跳多个目录,就能看懂一个功能

可独立修改

修改一个模块,不需要同步改其他模块

可独立删除

删除一个功能,不会留下"幽灵依赖"

如果做不到这三点,说明边界只是"看起来存在"。

为什么 Flutter 项目很容易边界失效

Flutter 在语法和工程上都非常"自由",这种自由在前期是优势,但也埋下了隐患。

import 没有限制

复制代码
import '../../services/order_service.dart';

任何地方都可以直接访问任何模块,结果就是:

  • UI 可以直接调 data 层
  • 工具类被全局依赖
  • 不同业务之间相互调用

边界一旦被跨越,就很难再收回来。

目录结构不等于模块结构

很多项目看起来分层清晰:

复制代码
pages/
services/
models/

但实际依赖是这样的:

复制代码
page A → service B → model C
page D → service B → model C

本质上:

这是按技术分层,而不是按业务分模块。

当业务增长,这种结构一定会交叉。

过早"抽公共",反而破坏边界

很多团队会很早建立:

复制代码
common/
shared/
utils/

初衷是复用,但结果往往是:

  • shared 越来越大
  • 所有模块都依赖它
  • 修改风险越来越高

最后变成一个典型问题:

公共层成了最大的耦合源。

一个更稳定的模块划分方式:按 Feature 拆

更适合长期维护的方式,是按业务拆分:

复制代码
lib/
 ├─ core/
 ├─ shared/
 └─ features/
     ├─ user/
     ├─ order/
     └─ home/

这里的关键点不是目录,而是边界规则

每个 Feature 必须"自闭环"

一个完整的 feature,应该长这样:

复制代码
order/
 ├─ data/
 │   ├─ order_api.dart
 │   ├─ order_dto.dart
 │   └─ order_repository.dart
 ├─ domain/
 │   └─ order_entity.dart
 ├─ ui/
 │   ├─ order_page.dart
 │   └─ widgets/
 └─ order_router.dart

核心原则:

一个功能的所有实现,都在一个目录内完成。

这样带来的好处是:

  • 新人只看一个目录就能理解功能
  • 改动范围天然被限制
  • 删除功能变得可行

依赖必须单向流动

必须明确一条规则:

复制代码
UI → Domain → Data

禁止:

  • data 调 UI
  • feature 之间直接调用内部实现

如果需要跨模块通信,可以用:

  • 抽象接口
  • 事件机制
  • 路由参数

本质是:

依赖只能向内收敛,不能横向扩散。

shared 只放"真正无业务"的代码

很多项目最大的问题就是 shared 失控,一个更严格的约束是,shared 只允许存在两类东西:

  • 完全无业务语义的 UI(比如基础按钮)
  • 纯工具函数(无状态、无依赖)

举个反例:

复制代码
UserCardWidget  (有业务语义)

正确做法是:

这种组件必须放在 user feature 内。

否则边界会逐渐被侵蚀。

如何判断边界是否健康

你可以用三个简单的问题做自检:

问题一:删除一个 feature,能否不改其他代码?

如果不能,说明存在隐性依赖。

问题二:新增一个功能,需要改多少旧代码?

如果修改范围很大,说明边界不清晰。

问题三:一个 bug 是否能快速定位模块?

如果需要全局搜索,说明结构已经失控。

一个常见误区:过度模块化

有些团队意识到问题后,会走向另一个极端:

  • 拆很多 package
  • 引入复杂依赖管理
  • 强行做"组件化"

结果是:

  • 开发效率下降
  • 调试成本变高
  • 团队难以适应

需要明确一点:

模块化不是目的,降低复杂度才是。

如果业务还没复杂到一定程度,过度设计反而是负担。

实际项目中的落地建议

结合实际开发经验,可以从这几个步骤开始优化:

第一步:停止扩展 shared

所有新功能优先放 feature 内

第二步:逐步迁移老代码

按业务把代码往 feature 收拢

第三步:在 code review 中约束依赖方向

这是最关键的一步

第四步:控制模块大小

一个 feature 太大时,再内部细分,而不是拆到全局

总结

模块边界这件事,很少有人在项目初期认真设计,但几乎所有团队都会在后期为此付出代价。

Flutter 给了我们足够的灵活性,但也意味着:

边界不会自动出现,只能靠人为约束。

当你开始用"业务"而不是"文件类型"去划分模块,用"依赖方向"而不是"目录层级"去约束代码时,项目才会真正从"能写",走向"能长期维护"。

相关推荐
世人万千丶21 小时前
Flutter 框架跨平台鸿蒙开发 - 恐惧清单应用
学习·flutter·华为·开源·harmonyos·鸿蒙
Utopia^21 小时前
Flutter 框架跨平台鸿蒙开发 - 21天挑战
flutter·华为·harmonyos
一直在想名1 天前
Flutter 框架跨平台鸿蒙开发 - 黑白屏
flutter·华为·kotlin·harmonyos
AI_零食1 天前
Flutter 框架跨平台鸿蒙开发 - 孤独指数应用
学习·flutter·开源·harmonyos
浮芷.1 天前
Flutter 框架跨平台鸿蒙开发 - 儿童技能打卡墙应用
科技·flutter·华为·harmonyos·鸿蒙
Utopia^1 天前
Flutter 框架跨平台鸿蒙开发 - 重力感知
flutter·华为·harmonyos
提子拌饭1331 天前
昼夜节律下的肝脏代谢清除率演算仪:基于鸿蒙Flutter的双路流场与酶解粒子对照架构
flutter·华为·架构·harmonyos·鸿蒙
小雨天気.1 天前
Flutter 框架跨平台鸿蒙开发 - 直觉训练器应用
flutter·华为·harmonyos
浮芷.1 天前
Flutter 框架跨平台鸿蒙开发 - 姿势纠正助手应用
科技·flutter·华为·harmonyos·鸿蒙
一直在想名1 天前
Flutter 框架跨平台鸿蒙开发 - 影子收藏家
flutter·华为·harmonyos