资讯动态

t3code三层架构实战:从代码分层到可维护性提升

发布时间:2026/10/27 23:15:44 来源:尧图企业网站定制
1. 从“t3code”这个代号说起它到底指什么第一次看到“t3code”这个词很多人会一头雾水。它不像“Python入门”“Docker实战”那样自带说明更像是一个圈内人才懂的暗号。我最初接触这个代号是在一个做前端工具链的小圈子里当时有人丢出一句“你那个 t3code 跑通了没”群里立刻炸出一堆讨论。后来我才慢慢摸清楚t3code 并不是某个官方框架的名字而是社区里对一类“三层代码结构”实践的口语化称呼——t 代表 three3 代表层级code 就是代码本身。说白了它描述的是一种把项目代码按职责切成三层的组织方式通常对应接口层、逻辑层、数据层也有人叫表现层、领域层、基础设施层。这个代号之所以能流传开是因为它精准戳中了一个痛点很多项目写着写着就变成一锅粥改一个按钮颜色要翻五个文件加一个字段要动七八处地方。t3code 的核心主张就是——让每一层只干一件事层与层之间通过明确的契约通信。它不绑定任何语言Java、TypeScript、Python、Go 都能用也不绑定任何框架Spring、NestJS、Django、Gin 都可以套。换句话说t3code 是一种架构习惯而不是一个可以 npm install 的包。那它适合谁呢如果你正在维护一个超过三千行的项目或者团队里超过三个人同时改同一份代码又或者你发现自己每次改需求都要“牵一发而动全身”那 t3code 这套思路就值得你花时间吃透。它尤其适合中小型团队从“能跑就行”向“可维护”过渡的阶段——大厂有成熟的架构组兜底小项目一个人写完全没必要分层但处在中间地带的项目t3code 往往是最划算的选择。接下来我会从它解决的问题、三层怎么切、每层怎么写、常见坑怎么避这几个角度把我在实际项目里踩过的路完整摊开。2. 为什么“三层”这个数字刚刚好2.1 一层和两层的致命缺陷先说说为什么不是一层。一层结构就是所有代码堆在一起路由、校验、业务计算、数据库查询全写在一个函数里。这种写法在项目初期效率极高我见过一个接口三十行搞定增删改查确实爽。但问题会在第三次需求变更时集中爆发产品说“这个字段要加密存储”你得在查询和写入两处都改测试说“这个逻辑要单独测”你发现它和数据库连接死死绑在一起根本没法 mock。一层结构的本质问题是变化频率不同的代码被绑在了一起——界面文案可能一天改三次而数据库表结构可能三个月才动一次把它们放一起等于让高频变化拖着低频变化一起跑。那两层呢很多人会自然想到“前端一层、后端一层”或者“Controller 一层、Service 一层”。两层比一层好但依然不够。我拿一个真实例子说明一个电商下单接口Controller 层负责收参数、返响应Service 层负责算价格、扣库存、写订单。看起来挺清晰但当你需要把“算价格”这段逻辑复用到另一个入口比如后台手动改价时你会发现它和“写订单”的事务绑在一起拆不出来。两层的问题在于业务逻辑和数据处理没有分开导致业务规则无法独立演进。t3code 之所以是三层而不是两层就是为了把“业务规则”这层单独拎出来让它不依赖任何数据库、不依赖任何 HTTP 框架纯粹到可以用一个普通函数测完。2.2 三层各自的边界怎么划三层听起来简单但真正难的是划边界。我见过太多项目号称三层结果逻辑层里塞满了 SQL 拼接数据层里又在做业务判断等于白分。我的经验是抓住一个核心问题这一层如果换掉其他层要不要跟着改接口层换掉比如从 REST 换成 GraphQL逻辑层和数据层不应该感知数据层换掉比如从 MySQL 换成 MongoDB逻辑层和接口层也不应该感知。如果换一层导致其他层大面积修改说明边界划错了。具体来说接口层的职责只有三件事收参数、做格式校验、把结果转成响应格式。它不应该知道“库存不足”这个业务规则只应该知道“逻辑层返回了一个错误码我把它转成 400 响应”。逻辑层的职责是所有业务规则的唯一住所价格怎么算、库存怎么扣、优惠券能不能叠加全在这里。它不应该出现SELECT、INSERT这种词也不应该出现req、res这种对象。数据层的职责是把逻辑层的意图翻译成存储操作逻辑层说“给我 ID 为 5 的用户”数据层负责去 MySQL 查、去 Redis 缓存、去文件读但绝不判断“这个用户是不是 VIP”。这三条边界守住了t3code 才算真正落地。2.3 一个反直觉的结论三层不是越多越好有人听完三层觉得有道理转头就搞出五层、七层什么“门面层”“转换层”“防腐层”全加上。我试过在一个小项目里搞五层结果一个简单的查询请求要穿过五个文件改一个字段名要动六处团队里新来的人三天没看懂调用链。t3code 的精髓不在于“层数多”而在于每层都有不可替代的职责。如果某一层只是把上一层的参数原样传给下一层那它就是多余的。我现在的判断标准很简单如果删掉这一层代码总量减少但可读性不降那就该删。三层是经过大量项目验证的“最小可行分层”再多就是过度设计。记住架构的目的是让改动变便宜不是让图看起来漂亮。3. 接口层别让“收参数”变成“写业务”3.1 参数校验放在这一层的理由接口层最容易犯的错是把业务校验和格式校验混在一起。比如“手机号必须 11 位”是格式校验应该放接口层“这个手机号已经注册过”是业务校验必须放逻辑层。为什么这么分因为格式校验是与入口强相关的——Web 入口要校验 JSON 字段CLI 入口要校验命令行参数消息队列入口要校验消息体它们的格式规则完全不同。而业务校验是与入口无关的——不管从哪进来“已注册手机号不能重复注册”这条规则都不变。如果把业务校验写在接口层那每加一个入口就要重写一遍迟早漏掉。我在实际项目里用过一个很实用的技巧接口层只做“类型和范围”校验逻辑层只做“状态和关系”校验。类型就是 string、number、boolean范围就是长度、大小、正则。状态和关系就是“这个 ID 是否存在”“这个状态是否允许操作”。这样分完之后接口层的代码几乎全是声明式的看一眼就知道这个接口收什么、返什么没有任何隐藏逻辑。逻辑层则全是 if-else 和领域判断测试的时候直接构造数据调函数就行不需要启动 HTTP 服务。3.2 响应格式的统一与例外接口层另一个重要职责是统一响应格式。我见过很多项目每个接口返回的 JSON 结构都不一样有的{code, data, msg}有的{success, result}前端每调一个接口就要写一套解析逻辑。t3code 建议在接口层做一个统一的响应包装器所有成功响应走同一个函数所有错误响应也走同一个函数。这样做的好处是前端只需要写一次拦截器而且日志、监控、埋点都可以在包装器里统一处理。但统一不等于死板。有些场景确实需要例外比如文件下载接口返回的是二进制流不能包成 JSON比如第三方回调接口要求返回特定格式的字符串。我的做法是默认统一例外显式声明。在代码里用一个RawResponse之类的标记或者单独的 handler 来区分而不是让每个开发者自己决定。这样既保证了 95% 的接口格式一致又给 5% 的特殊场景留了口子。关键是这个口子要看得见——代码审查时一眼能发现“哦这个接口是特殊的”而不是藏在某个角落没人知道。3.3 接口层不该出现的三类代码根据我踩过的坑接口层有三类代码一旦出现就要立刻警惕。第一类是数据库查询语句比如在 Controller 里直接写db.query(SELECT ...)这意味着逻辑层被架空了业务规则散落在接口层。第二类是复杂的条件分支比如if (user.level 3 order.amount 100 ...)这种判断属于业务规则应该下沉到逻辑层。第三类是事务控制比如在接口层开事务、提交事务这会导致逻辑层无法被复用——因为逻辑层不知道自己在不在事务里。我遇到过一个典型案例一个接口在接口层开了事务然后调用逻辑层的“创建订单”方法逻辑层内部又调用了“扣库存”方法。后来另一个入口也需要“扣库存”直接调逻辑层结果因为没有事务导致库存扣了但订单没创建。修复方法就是把事务控制移到逻辑层让逻辑层自己决定事务边界。接口层只负责“调用逻辑层并返回结果”不参与任何事务决策。这个教训让我明白接口层越薄越好薄到像一张纸透过去能直接看到逻辑层的轮廓。4. 逻辑层业务规则的唯一住所4.1 逻辑层如何做到“不依赖任何框架”逻辑层是 t3code 的心脏也是最难写好的部分。它的理想状态是把这一层的代码复制到一个空项目里不装任何框架也能跑通单元测试。这意味着逻辑层不能 import 任何 Web 框架的类不能 import 任何数据库驱动不能 import 任何缓存客户端。它只依赖两样东西领域模型和数据层接口。领域模型就是那些纯数据的类比如Order、User、Coupon数据层接口就是一组抽象方法比如findUserById、saveOrder。为什么这么强调“不依赖框架”因为框架是会变的。我经历过从 Express 迁移到 NestJS 的项目如果逻辑层绑定了 Express 的Request对象迁移时就要重写所有逻辑。但如果逻辑层只接收普通对象迁移时只需要改接口层的适配代码。同样数据库从 MySQL 换到 PostgreSQL如果逻辑层直接写 SQL迁移就是灾难如果逻辑层只调用userRepository.findByEmail()换数据库只需要换一个实现类。这种“依赖倒置”的写法是 t3code 能长期存活的关键。4.2 领域模型该放什么、不该放什么领域模型是逻辑层的核心资产但很多人把它写成“数据库表的映射”这是最大的误区。数据库表是存储结构领域模型是业务概念两者不应该一一对应。比如数据库里可能有一张user_profile表和一张user_account表但在业务里它们就是一个User对象。领域模型应该包含业务属性和业务行为而不是存储字段。举个例子Order模型里应该有totalAmount属性但不应该有createdAtTimestamp这种纯存储字段——后者是数据层的事。我习惯在领域模型里放三类东西状态字段比如status、amount、业务方法比如order.canCancel()、order.applyDiscount()、不变量校验比如构造函数里检查金额不能为负。业务方法的好处是把规则收拢到模型内部而不是散落在各个 service 里。比如“订单能否取消”这个判断如果写在 service 里每个调用方都要自己判断如果写成order.canCancel()所有调用方都调同一个方法规则变了只改一处。这就是 t3code 强调的“把行为绑在数据上”。4.3 逻辑层的事务边界怎么定事务是逻辑层最棘手的问题。我的原则是一个业务用例一个事务。什么叫业务用例就是“用户下单”这件事它可能包含扣库存、创建订单、发优惠券三个步骤这三个步骤要么全成功要么全失败所以它们在一个事务里。而“用户查询订单”是另一个用例它不需要事务。判断标准是这个操作是否改变了系统状态且多个改变之间是否必须原子。具体实现上我倾向于在逻辑层的入口方法上开事务而不是在数据层的方法上开。因为数据层的方法可能被多个逻辑层方法复用如果每个数据层方法自己开事务会导致事务嵌套或者事务粒度过细。比如saveOrder和deductStock如果各自开事务那么“下单”这个用例就无法保证原子性。正确做法是在逻辑层的placeOrder方法上开一个事务然后依次调用saveOrder和deductStock它们共享同一个事务。这样事务边界清晰而且逻辑层完全掌控了“什么算一个完整业务”。5. 数据层把“存储”这件事关进笼子5.1 数据层接口的设计原则数据层在 t3code 里扮演的是“翻译官”角色把逻辑层的业务语言翻译成存储引擎能听懂的操作语言。它的接口设计应该以业务需求为导向而不是以数据库表为导向。什么意思如果逻辑层需要“查找所有超过 30 天未登录的用户”数据层就应该提供一个findInactiveUsers(days)方法而不是让逻辑层自己去拼WHERE last_login ?。前者是业务语言后者是存储语言。把存储语言关在数据层里逻辑层才能保持干净。我设计数据层接口时有个习惯方法名用业务动词参数用业务对象返回值用领域模型。比如findActiveSubscriptions(userId)返回Subscription[]而不是返回RowDataPacket[]。这样逻辑层拿到返回值就能直接用不需要再做一次转换。同时数据层内部可以自由优化——今天用 MySQL 查明天加 Redis 缓存后天换成 Elasticsearch只要接口不变逻辑层完全无感。这就是“把变化关进笼子”的价值。5.2 缓存该放在哪一层缓存的位置是很多团队争论的焦点。有人放接口层有人放逻辑层有人放数据层。我的经验是缓存属于数据层。因为缓存的本质是“存储的加速器”它和数据库、文件、外部 API 一样都是数据的来源。逻辑层不应该知道“这个数据是从缓存来的还是从数据库来的”它只关心“我拿到了数据”。如果把缓存放在逻辑层逻辑层就会混入if (cache.has(key))这种存储细节违背了 t3code 的边界原则。但缓存放在数据层不等于“无脑加缓存”。我见过最糟糕的做法是在每个数据层方法里都加一层 Redis 查询导致代码里一半是缓存逻辑。正确做法是在数据层内部做缓存策略比如用一个装饰器模式基础实现是数据库查询缓存实现包在外面先查缓存再查数据库。这样逻辑层调用的还是同一个接口但底层已经自动带了缓存。缓存失效策略也由数据层自己管理比如“用户更新时删除缓存”这个逻辑放在数据层的saveUser方法里而不是让逻辑层去手动删缓存。5.3 数据层如何应对多存储源现代项目很少只用一种存储。MySQL 存核心数据Redis 存会话Elasticsearch 做搜索对象存储放文件。t3code 的数据层需要把这些差异全部吸收掉。我的做法是按业务领域拆分数据层实现而不是按存储类型拆分。比如UserRepository这个接口它的实现类UserRepositoryImpl内部可能同时用了 MySQL 和 Redis读用户基本信息走 MySQL读用户在线状态走 Redis。逻辑层只调userRepository.findById()完全不知道背后用了两种存储。这种设计的好处是存储选型可以独立演进。今天用户量小全部走 MySQL明天用户量大了加一层 Redis 缓存后天要做全文搜索加一个 Elasticsearch 实现。每次变化只影响数据层的某个实现类逻辑层和接口层纹丝不动。我经历过一个项目从 MySQL 迁移到 MongoDB因为数据层接口设计得好迁移只花了三天逻辑层一行没改。这就是 t3code 分层带来的实际收益。6. 落地 t3code 时最容易踩的四个坑6.1 坑一为了分层而分层代码量翻倍我见过最极端的案例是一个简单的“查询用户列表”接口被拆成了UserController、UserService、UserServiceImpl、UserRepository、UserRepositoryImpl、UserConverter、UserDTO、UserVO八个文件。一个查询请求要穿过八层改一个字段名要动六个文件。这种“为了分层而分层”的做法完全违背了 t3code 的初衷。t3code 的目的是让改动变便宜如果分层导致改动更贵那就说明分错了。我的判断标准是如果两个层之间的代码只是简单的参数传递没有任何逻辑转换那就合并。比如UserService只是调UserRepository然后返回没有任何业务逻辑那这个 Service 就是多余的。同样如果UserDTO和UserVO字段完全一样只是名字不同那也没必要分开。t3code 的三层是逻辑分层不是文件分层。一个文件里可以同时有接口层和逻辑层的代码只要它们职责清晰、边界明确。关键是思维上分层而不是物理上分文件。6.2 坑二逻辑层偷偷依赖了框架这是最隐蔽的坑。逻辑层表面上没有 import 框架但代码里用了框架的全局状态。比如在逻辑层里直接调ConfigService.get(db.host)或者用了AsyncLocalStorage这种框架提供的上下文。这些依赖不会在 import 语句里暴露但会让逻辑层无法脱离框架运行。我踩过一次坑逻辑层里用了 NestJS 的Logger结果单元测试时因为没启动 Nest 容器日志直接报错。后来改成注入一个Logger接口测试时传一个空实现问题才解决。检查逻辑层是否干净有个简单方法把逻辑层的代码复制到一个空的 TypeScript 项目里只装类型定义不装任何框架看能不能编译通过。如果编译报错说找不到某个模块说明逻辑层依赖了外部框架。这个方法我每次重构都会用屡试不爽。逻辑层应该只依赖标准库和自己定义的接口其他一切都要通过依赖注入传进来。6.3 坑三数据层返回了“半成品”数据层返回的数据结构直接决定了逻辑层的写法。如果数据层返回的是数据库原始行比如{ user_name: 张三, user_age: 20 }逻辑层就要做字段名转换、类型转换、空值处理这些杂活会污染逻辑层。我见过一个项目数据层返回的日期是字符串逻辑层每次用都要new Date()转一次后来有个地方忘了转导致时间比较出错。正确的做法是数据层返回完整的领域模型字段名、类型、嵌套结构全部对齐业务概念。具体来说数据层在查询到原始数据后应该立刻做一次“组装”把user_name转成name把字符串日期转成Date对象把关联表的数据组装成嵌套对象。这样逻辑层拿到的就是“开箱即用”的模型不需要再做任何转换。这个组装过程可以放在数据层的toDomain()方法里也可以用一个专门的 Converter。关键是转换只做一次且只在数据层做。逻辑层如果还在做字段转换说明数据层没做到位。6.4 坑四三层之间的调用链变成“迷宫”t3code 的调用方向应该是单向的接口层调逻辑层逻辑层调数据层。但实际项目中经常出现反向调用或者跨层调用。比如数据层为了“方便”直接调了逻辑层的某个方法或者接口层绕过逻辑层直接调数据层。这些都会让调用链变成迷宫改一处不知道会影响哪里。我见过一个项目接口层直接调数据层做查询逻辑层又调接口层做参数校验整个依赖关系乱成一团最后没人敢改代码。防止调用链混乱我的做法是用目录结构强制约束。比如src/api/只允许 importsrc/domain/src/domain/只允许 importsrc/infra/src/infra/不允许 import 上面两层。然后用 ESLint 的no-restricted-imports规则来强制检查违反就报错。这样从物理上杜绝了反向依赖。另外每个层只暴露必要的接口比如数据层只暴露 Repository 接口不暴露具体的数据库连接对象这样上层想绕也绕不过去。7. 一个真实项目的 t3code 改造记录7.1 改造前的代码状态去年我接手了一个运营后台项目代码量大概一万五千行用的是 Express MySQL。改造前的状态是典型的“一层结构”所有逻辑都在路由处理函数里一个文件平均八百行最长的文件两千三百行。改一个需求平均要动三个文件而且经常改完 A 功能坏了 B 功能。测试覆盖率不到 10%因为逻辑和数据库绑死根本没法写单元测试。团队三个人每次发版前都要全员加班回归测试。我印象最深的一次事故产品要求“优惠券金额不能超过订单金额”开发在路由里加了一个if判断。结果另一个入口后台手动改价没有加这个判断导致出现了负金额订单。排查时发现同样的业务规则在三个地方各写了一遍改的时候只改了一处。这就是一层结构的典型问题业务规则没有唯一住所散落在各个角落迟早会漏。7.2 改造步骤与时间分配改造分四步走总共花了三周。第一周做接口层剥离把路由处理函数里的参数校验、响应格式化抽出来放到独立的中间件和工具函数里。这一步不改变业务逻辑只是把“收参数”和“返响应”的代码从业务代码里拆出来。第二周做逻辑层提取把路由处理函数里的业务判断、计算逻辑抽到独立的 service 文件里service 只接收普通对象不接收req和res。这一步最耗时因为要理清哪些是业务逻辑、哪些是数据操作。第三周做数据层封装把所有 SQL 语句从 service 里抽出来放到 repository 文件里service 只调 repository 的方法。同时把数据库返回的原始行转换成领域模型。第四周做测试补全给逻辑层写单元测试因为逻辑层已经不依赖数据库了测试写起来很快一周补了 60% 的覆盖率。改造完成后改一个需求平均只动一个文件发版前的回归测试从两天缩短到半天。7.3 改造后的收益与遗留问题改造后最明显的收益是新人上手时间从两周缩短到三天。因为代码结构清晰新人只要看懂接口层就知道系统有哪些功能看懂逻辑层就知道业务规则是什么看懂数据层就知道数据从哪来。另一个收益是单元测试变得可行逻辑层的测试不需要启动数据库跑一次只要几秒钟开发过程中可以频繁运行。线上事故率也明显下降因为业务规则收拢到了逻辑层不会再出现“改一处漏一处”的情况。但改造也留下了两个问题。一是性能略有下降因为多了一层对象转换接口响应时间从平均 30ms 增加到 45ms。不过这个代价换来的是可维护性我认为值得。二是部分复杂查询仍然绕过了逻辑层因为有些报表查询需要多表联查硬套三层反而麻烦。我的处理方式是允许例外但例外要显式标记在代码里加注释说明“此处为性能优化直接查询数据库”而不是偷偷绕过。这样既保证了 90% 的代码遵循 t3code又给 10% 的特殊场景留了空间。8. 关于 t3code 的几个常见疑问8.1 小项目有必要用 t3code 吗我的答案是看项目预期寿命。如果这个项目只跑一个月就下线或者只是个人练手那完全没必要分层一层结构写起来最快。但如果这个项目预期要维护半年以上或者会有其他人接手那 t3code 就值得投入。我见过太多“临时项目”最后变成“长期项目”的例子一开始图快不分层三个月后改不动了重构的成本比一开始分层高十倍。所以我的建议是如果不确定项目会不会长期存在就按长期项目来写因为分层的成本其实很低一个接口多写两个文件而已。8.2 t3code 和 MVC 有什么区别很多人会把 t3code 和 MVC 混为一谈其实它们关注的点不同。MVC 关注的是用户界面和数据的交互Model 是数据View 是界面Controller 是协调者。t3code 关注的是业务逻辑和存储的隔离它不关心界面只关心“业务规则写在哪”和“数据怎么存取”。一个项目可以同时用 MVC 和 t3codeMVC 的 Controller 对应 t3code 的接口层MVC 的 Model 对应 t3code 的逻辑层加数据层。但 t3code 比 MVC 更强调逻辑层不依赖框架这是 MVC 没有明确要求的。8.3 逻辑层要不要拆成多个 service这个问题没有标准答案我的经验是按业务领域拆不按技术职责拆。比如电商项目可以拆成OrderService、UserService、PaymentService每个 service 负责一个领域的业务规则。不要拆成ValidationService、CalculationService、PersistenceService因为这样拆会导致一个业务用例要调多个 service事务边界很难定。按领域拆的好处是一个业务用例通常只涉及一个 service事务边界清晰代码也容易理解。如果某个 service 太大可以再按子领域拆比如OrderService拆成OrderCreateService和OrderQueryService但这是优化不是必须。8.4 数据层要不要做 DTO 转换要而且必须在数据层做。我见过有些项目在逻辑层做 DTO 转换结果逻辑层里一半代码是user.name row.user_name这种赋值。这些转换应该全部下沉到数据层逻辑层拿到的就是干净的领域模型。转换的时机是数据层从数据库拿到原始数据后立刻转而不是等到逻辑层用的时候再转。这样逻辑层完全不知道数据库字段名是什么换数据库时逻辑层无感。转换的代码可以放在 repository 的实现类里也可以单独写一个 Converter 类关键是转换只做一次且只在数据层做。9. 我个人的几条实操心得第一条心得先写逻辑层再写接口层和数据层。很多人习惯从接口层开始写先定义路由再写逻辑最后写数据库。但这样容易导致逻辑层被接口层的需求牵着走写出“为了这个接口定制”的逻辑。我的做法是先想清楚业务规则是什么把逻辑层写出来然后用单元测试验证。逻辑层稳定后再写接口层去适配它写数据层去支撑它。这样逻辑层是核心接口层和数据层是外围核心稳定了外围怎么变都不怕。第二条心得用接口来定义层与层之间的契约。接口层调逻辑层时不要直接调具体的类而是调一个接口。逻辑层调数据层时也是调接口。这样每一层都可以独立替换实现测试时也可以传 mock 实现。我习惯在每一层都定义一个types.ts或者interfaces.ts把这一层对外暴露的接口写清楚。这样新人看接口文件就知道这一层能做什么不需要翻实现代码。第三条心得不要追求一步到位。t3code 是一个渐进的过程不要想着一次性把整个项目重构完。我的做法是新代码严格按三层写老代码改到哪就顺手分层。比如今天要改一个老接口就顺便把它的逻辑抽到 service 里把 SQL 抽到 repository 里。这样半年下来老代码也慢慢分层了而且每次改动都有测试覆盖风险可控。一次性重构一万五千行代码大概率会引入新 bug而且团队会怨声载道。第四条心得分层不是目的可维护才是。我见过一些团队把 t3code 当成教条每个接口必须三个文件每个文件必须不超过一百行。结果为了凑文件数把简单的逻辑拆得七零八落反而更难读。t3code 的本质是让变化发生在一处如果某个项目就是很简单一个文件能写完那就一个文件写完。架构是为人服务的不是人为架构服务。当你发现分层让代码更难懂了那就是分错了赶紧调整。最后分享一个检查分层是否合理的小技巧随便挑一个最近改过的需求看这次改动动了几个文件。如果只动了一个文件说明分层合理如果动了三个以上说明边界没划好需要重新审视。这个指标比任何架构图都直观因为改动的文件数直接反映了“变化的传播范围”。t3code 的目标就是让变化传播范围最小改一个功能只动一个文件这就是最好的分层。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑