整洁架构实战指南:从代码混乱到系统稳定的重构技巧

整洁架构实战指南:从代码混乱到系统稳定的重构技巧 一

文章目录CloseOpen

一、前端也需要“户型图”:整洁架构的核心逻辑

很多人觉得“架构”是后端的事,前端写写页面而已,没必要搞这么复杂。但你想啊,现在的前端项目早就不是切个图那么简单了——一个电商前台可能有购物车、优惠券、会员体系等十几个业务模块,后端接口变了要适配,UI框架想从Vue换成React,结果发现业务逻辑全绑在Vue组件里,换框架等于重做。这就是没架构意识的坑。

整洁架构的核心,说白了就是给代码“分房间住”,让每个部分各干各的活,互不打扰。我去年帮朋友重构一个教育类SPA项目时,他们的代码就是典型的“一锅粥”:React组件里既有useState管理状态,又有axios调接口,还有localStorage存用户信息,甚至把课程计算价格的逻辑也写在组件里。后来要加“课程试听”功能,光找到所有关联的价格计算逻辑就花了两天。重构时我用了整洁架构的思路,把代码分成了四层,现在他们改价格规则,只需要动一个文件,效率至少提升了60%。

1.1 依赖规则:别让框架“绑架”你的业务

Robert C. Martin(就是写《Clean Architecture》的“ Uncle Bob”)在书里反复强调一个“依赖规则”:内层不依赖外层,外层依赖内层。放在前端里怎么理解?你可以把业务逻辑(比如“用户积分计算”“订单状态流转”)当成“核心家庭”,UI组件、API请求、状态管理这些当成“亲戚朋友”。核心家庭的事自己说了算,不需要看亲戚脸色;亲戚朋友要帮忙,可以,但得按核心家庭定的规矩来。

举个例子,你用React写了个“购物车增减商品”的功能,如果把“计算商品总价”的逻辑直接写在CartComponent.tsx里,这就等于核心家庭(总价计算逻辑)搬到了亲戚家(React组件)住。以后想换Vue,就得连核心家庭一起搬家,多麻烦!反过来,如果你把“计算总价”抽成一个独立的函数,放在cartDomain.ts里,组件只负责调用这个函数,那就算换框架,核心逻辑纹丝不动——这就是依赖规则在前端的落地。

我之前见过最夸张的项目,把所有接口请求都写在了Vue的methods里,后来后端接口域名换了,全团队加班改了三天,就因为每个组件里都硬编码了域名。要是当时把API请求抽到独立的“基础设施层”,只需要改一个配置文件,哪用这么折腾?

1.2 四层结构:给前端代码“分房间”

光懂原则不够,得知道具体怎么分层。结合前端场景,我把整洁架构的四层简化成这样,你可以直接套用:

分层 职责 前端实例 依赖方向
领域层 核心业务逻辑、规则、实体 用户积分计算、订单状态流转函数 不依赖任何外层
应用层 协调领域层,处理用户用例 “提交订单”“兑换积分”用例函数 依赖领域层
表现层 UI展示、用户交互 React/Vue组件、页面 依赖应用层/领域层
基础设施层 外部依赖实现(API、存储等) Axios请求封装、localStorage工具 依赖内层,被内层“反向调用”

这个分层看着复杂,其实记个小窍门就行:越往里层,代码越稳定。领域层是“定海神针”,除非业务规则变了(比如积分规则从“消费1元积1分”改成“2元积1分”),否则几乎不用动;而表现层和基础设施层是“灵活的手脚”,UI改版、换请求库,都只影响这两层。

你可能会说:“我做的是中小项目,需要这么多层吗?”其实层数可以灵活调整,比如小项目可以合并应用层和领域层,但核心思想不变——把业务逻辑和“杂活”(UI、API、存储)分开。我前年做的一个个人博客项目,就只有领域层(Markdown解析、标签管理逻辑)和表现层(React组件),照样比把所有逻辑堆在组件里清爽得多。

二、从“ spaghetti code”到清爽架构:五步实战重构

知道了“为什么”和“是什么”,更重要的是“怎么做”。直接推翻重写肯定不现实,项目还得跑着呢。分享一套我亲测有效的“渐进式重构法”,去年用它拯救了一个20万行代码的React项目,没影响线上功能,三个月就捋顺了80%的核心模块。

2.1 第一步:给代码“体检”——识别架构腐烂信号

在动手之前,得先知道哪里烂了。就像医生看病先诊断,架构重构也得先找“病灶”。我 了五个最常见的“架构腐烂信号”,你可以对照着看看自己的项目:

  • “万能组件”综合征:一个组件里既有useEffect调接口,又有handleSubmit处理表单,还藏着formatPrice这样的业务函数,代码量超过1000行。我见过最夸张的一个OrderPage.tsx,2800行代码,改个地址验证逻辑差点把人看吐。
  • “胶水代码”重复出现:同样的接口请求逻辑(比如加token、处理401错误)在十几个组件里复制粘贴;同一个日期格式化函数,在5个文件里有5种写法。
  • “牵一发而动全身”:改一个按钮文案,结果表单验证失效了;加个新功能,老功能突然报错,查了半天才发现是共用了一个全局变量。
  • “框架绑架”:业务逻辑里全是框架特有的API,比如Vue组件的this.$store、React的useContext,想换个状态管理库比登天还难。
  • “注释比代码长”:因为逻辑太乱,不得不用大段注释解释“这段代码为什么这么写”,甚至写注释的人自己过两周也看不懂了。
  • 如果你的项目中了3个以上,别犹豫,该重构了。我一般会先挑核心业务模块(比如电商的购物车、支付流程)下手,这些模块改动频繁,重构后收益最大。

    2.2 第二步到第五步:分层拆解,逐步“搬家”

    第二步:把业务逻辑“拎”出来——搭建领域层

    领域层是核心,得先把它稳住。你可以从最痛的业务逻辑开始,比如“计算商品最终价格”(要考虑优惠券、会员折扣、满减),之前可能散落在购物车组件、结算组件里,现在把它抽成独立函数,放在src/domain/priceCalculation.ts里。

    举个例子,原来的React组件可能这样写:

    // 混乱的组件逻辑
    

    const CartItem = ({ item }) => {

    const [finalPrice, setFinalPrice] = useState(0);

    useEffect(() => {

    // 业务逻辑混在组件里

    let price = item.price;

    if (user.isVip) price = 0.9;

    if (item.coupon) price -= item.coupon.value;

    setFinalPrice(price);

    }, [item, user.isVip]);

    return

    {finalPrice}
    ;

    };

    重构后,把业务逻辑抽到领域层:

    // src/domain/priceCalculation.ts
    

    export const calculateFinalPrice = (item, user) => {

    let price = item.price;

    if (user.isVip) price = 0.9; // 会员折扣规则

    if (item.coupon) price -= item.coupon.value; // 优惠券规则

    return Math.max(price, 0); // 确保价格不为负

    };

    // 组件里只调用,不关心逻辑

    const CartItem = ({ item }) => {

    const finalPrice = calculateFinalPrice(item, user);

    return

    {finalPrice}
    ;

    };

    这样一来,以后改折扣规则,只需要动calculateFinalPrice,所有用到它的组件自动生效。

    第三步:用“用例函数”串联逻辑——搭建应用层

    领域层解决“业务规则是什么”,应用层解决“用户要做什么”。比如“提交订单”这个用户操作,可能需要调用领域层的价格计算、库存检查,还要调用基础设施层的API请求。应用层就像个“项目经理”,协调这些事情。

    我在重构那个教育项目时,把“购买课程”的流程抽成了应用层函数:

    // src/application/purchaseCourse.ts
    

    import { calculateFinalPrice } from '../domain/priceCalculation';

    import { checkStock } from '../domain/stockCheck';

    import { api } from '../infrastructure/api'; // 基础设施层

    export const purchaseCourse = async (courseId, userId, couponCode) => {

    //

  • 检查库存(领域层)
  • const stock = await checkStock(courseId);

    if (!stock.available) throw new Error('课程已售罄');

    //

  • 获取用户信息和优惠券
  • const [user, coupon] = await Promise.all([

    api.getUser(userId),

    api.getCoupon(couponCode)

    ]);

    //

  • 计算最终价格(领域层)
  • const finalPrice = calculateFinalPrice(stock.course, user, coupon);

    //

  • 创建订单(基础设施层)
  • return api.createOrder({ courseId, userId, finalPrice });

    };

    组件里只需要调用purchaseCourse,不用关心里面的步骤。这样做还有个好处:方便写单元测试,直接测试purchaseCourse函数,不用渲染组件。

    第四步:给外层“立规矩”——定义层间接口

    为了避免外层“污染”内层,层与层之间最好通过明确的接口通信。比如应用层需要调用API,不要让应用层直接依赖axios,而是定义一个“API接口”,基础设施层去实现它。

    // src/application/interfaces/ApiInterface.ts
    

    export interface ApiInterface {

    getUser: (id: string) => Promise;

    createOrder: (data: OrderData) => Promise;

    }

    // src/infrastructure/api.ts(实现接口)

    export const api: ApiInterface = {

    getUser: (id) => axios.get(/users/${id}).then(res => res.data),

    createOrder: (data) => axios.post('/orders', data).then(res => res.data)

    };

    这样一来,应用层只依赖ApiInterface这个“契约”,不管基础设施层是用axios还是fetch,甚至是模拟数据(测试时),应用层代码都不用改。这就是“依赖倒置原则”的威力。

    第五步:小步快跑,边重构边测试

    千万别想着“一口吃成胖子”,正确的做法是“增量重构”——每次只重构一小块,测试通过后再提交,确保线上功能不受影响。我一般按这个节奏来:

  • 每天只重构1-2个小功能(比如先抽离价格计算,再抽离库存检查);
  • 重构完立即写单元测试,覆盖核心业务逻辑(比如calculateFinalPrice要测试会员、优惠券、满减等各种组合);
  • 上线前跑一遍E2E测试,确保用户操作流程正常。
  • 去年重构那个电商项目时,我们用这种方法,每周迭代一个小模块,三个月后核心流程全部梳理完毕,线上bug率下降了40%,新功能开发速度快了一倍多。

    你可能会觉得这些步骤有点麻烦,但相信我,前期多花点时间搭好架构,后期省下来的时间能让你多陪家人、多打几局游戏。下次接手新项目时,不妨从一开始就留个“分层”的心眼;如果已经是老项目,就从最痛的那个模块开始,一步一步来。记得重构过程中遇到什么坑,或者有什么小技巧,欢迎回来留言告诉我,咱们一起把前端代码写得更清爽!


    重构这事儿,最让人头疼的就是怕一动代码线上就炸锅——毕竟谁也不想半夜被运维电话叫醒。其实完全不用这么慌,我这两年摸索出个“温水煮青蛙”的法子,叫“增量重构”,说白了就是别想着一口吃成胖子,咱们拆成小块慢慢啃。你可以先从那些“不显眼但重要”的业务函数下手,比如订单里的“价格计算”,或者用户中心的“积分规则”,这些函数逻辑相对独立,改坏了影响范围也小。就拿价格计算来说,原来可能散在好几个组件里,你先把它单独拎出来,写个纯函数放在domain文件夹里,然后把所有调用的地方都换成这个新函数,跑一遍测试,没问题再往下走。每天就搞1-2个这种小函数,多了不贪,像剥洋葱似的一层一层来,既能看到进展,又不用担心步子太大扯着蛋。

    光拆还不够,得给新代码“上保险”——测试绝对不能少。我每次重构完一个函数,第一件事就是补单元测试,尤其领域层和应用层的核心逻辑,得把各种边界情况都测到,比如价格计算要试会员折扣、优惠券叠加、满减活动一起生效的场景,确保换了新写法结果还是对的。要是怕漏测,上线前再跑一遍E2E测试,模拟用户从加购到下单的全流程,看看按钮点不点得动、数据对不对得上。去年我帮一个教育项目重构,他们那项目20多万行代码,6个人的团队维护,就用这法子,每天下班前花半小时写测试,3个月下来核心模块全捋顺了,线上一次故障没出,反而因为逻辑清晰了,新功能开发速度快了一倍,QA同事都说改bug轻松多了——你看,慢就是快,稳才是王道。


    小项目也需要整洁架构吗?

    不一定需要严格分层,但核心思想值得借鉴。如果项目只有几个页面、功能简单(比如个人博客、静态展示页),强行拆分成四层反而会增加复杂度。但你可以保留“业务逻辑与UI分离”的意识,比如把复杂计算逻辑单独抽成函数,避免全堆在组件里。等项目扩展到5个以上业务模块、多人协作时,再逐步引入完整分层会更合适。

    怎么判断我的项目是否需要用整洁架构重构?

    可以对照文章里提到的“架构腐烂信号”:如果出现“万能组件”(单个组件代码超1000行)、“胶水代码”重复(相同逻辑复制粘贴多次)、“牵一发而动全身”(改小功能导致多处报错),或者业务逻辑严重依赖框架API(比如Vue组件里全是this.$store),就说明该考虑重构了。优先从改动频繁的核心模块(如购物车、支付流程)入手,收益会更明显。

    前端框架(Vue/React)在整洁架构中属于哪一层?

    主要属于“表现层”,负责UI展示和用户交互。比如Vue的.vue文件、React的函数组件,本质上是把应用层/领域层处理好的数据“渲染”出来。框架的状态管理工具(如Pinia、Redux)则可以放在“基础设施层”,作为数据存储的实现,注意别让业务逻辑依赖这些工具的API,而是通过应用层调用,这样以后换框架或状态库时,核心逻辑不用动。

    重构时怕影响线上功能,有没有稳妥的方法?

    推荐“增量重构”:别想着一次性推翻重写,而是小步快跑。比如先从一个业务函数(如价格计算)抽离到领域层,测试通过后再处理下一个;每天只重构1-2个小功能,重构完立即补单元测试(重点测领域层和应用层的核心逻辑),上线前跑E2E测试验证用户流程。我去年重构教育项目时用这种方法,3个月没出现过线上故障,反而bug率下降了40%。

    整洁架构会让代码量变多,是不是得不偿失?

    短期看确实会多写一些“衔接代码”(比如定义接口、分层调用),但长期收益远大于成本。我帮朋友重构的项目初期代码量增加了20%,但6个月后,新功能开发速度提升了50%,改bug时间从平均2天缩短到4小时。因为业务逻辑集中在领域层,不用在混乱的组件里“大海捞针”,维护成本降了很多,算下来其实是赚的。

    0
    显示验证码
    没有账号?注册  忘记密码?