从低代码桎梏到纯代码重构:MBA培训系统架构选型与全栈脚手架实践

📅 发布时间:2026/10/9 2:26:21
从低代码桎梏到纯代码重构:MBA培训系统架构选型与全栈脚手架实践
1. 为什么放着现成的低代码不用非要纯代码重构先说背景。前一阵我接手了某商学院培训中心的MBA培训管理系统改造需求。旧系统是在一个低代码平台上搭的表面上看表单、列表、权限都能拖出来运营团队也用了两三年。但随着业务越做越深问题开始集中爆发报名表字段改不动、课程排期和教室资源关联全靠手工、财务对账要从系统里导出Excel再二次加工、偶尔想加一个自动发送通知的规则平台的脚本能力又跟不上。最难受的是数据库完全被平台锁死想自己写报表要绕好几层接口性能还特别差。所以这次重构团队内部最开始争论的是要不要继续在原平台上缝缝补补我当时的态度很明确——如果是风险低、流程固定的内部小工具低代码确实快但MBA培训这种业务天然带着高频变化报名、排课、师资、教务、财务、学员服务之间耦合很深继续在低代码平台里硬撑后面每改一个需求都要和平台斗智斗勇。与其在别人的框框里跳舞不如自己做一次彻底的纯代码重构把数据和逻辑都掌握在自己手里。这篇是重构系列的第一篇先把地基打好。整个系列我打算按照“架构选型与脚手架搭建 → 核心领域模型设计 → 报名与课程排期模块 → 角色权限与审批流 → 报表与消息通知 → 迁移与上线”的节奏来写。这一篇聚焦在最开头的事情架构怎么选、技术栈怎么定、前后端脚手架怎么搭、本地开发环境怎么一键跑起来。适合正在做业务系统重构的开发者、准备从低代码平台转向纯代码开发的团队参考也适合想系统了解一个全栈项目从零开始的读者。有人会问重构一个MBA培训管理系统为什么值得单独写一篇架构选型的文章因为这套系统看起来只是个普通的业务管理系统实际上它的业务特征很鲜明一是报名季流量有尖峰一个班几十上百人报名集中在某几天涌入二是数据敏感度高涉及学员个人信息、缴费记录三是审批链路长从课程立项、开课申请到调课、退费每一环都要留痕四是报表需求复杂管理者要看招生转化、班级满员率、师资负荷。这些特征直接决定了架构选型的方向不能拍脑袋选一套热门技术就开干。所以我会先花一章讲清楚选型背后的逻辑再动手搭脚手架。1.1 旧系统踩过的坑这套旧系统让我印象最深的几个问题值得先摆出来。第一是数据不可迁移。低代码平台把数据表封装在自己的元数据模型里虽然能通过接口导出但导出的是扁平化的JSON原本表之间的关联关系、字段约束全都丢了而且导出过程受平台限流影响几万行数据导一次要跑很久。第二是业务规则写不进去。比如“同一学员在同一个学期内不能重复报同一门选修课”这种规则在低代码平台里要靠表单校验后端脚本人工复核三道工序才能勉强实现而且一旦平台升级脚本语法原来的逻辑可能直接失效。第三是性能天花板太低。旧系统里一个简单的课程列表页因为要动态渲染权限范围内的数据接口响应经常超过三秒到了报名高峰期后台任务还会把整个平台拖慢所有用户一起卡。第四是集成能力弱微信通知、企业支付、短信验证码这类外部服务在低代码平台里要么靠官方插件要么靠写平台特有的脚本通用性很差换个服务商就得改一版。这些坑几乎是所有低代码系统做到一定规模后的通病。所以纯代码重构不是炫技也不单是为了“代码在手天下我有”的心理满足而是因为业务已经走到了需要掌控数据模型、掌控运行逻辑、掌控性能表现的阶段。这是我建议任何同类系统做重构时先想清楚的第一件事你到底是在换技术还是在解决业务发展的瓶颈如果答案是后者那纯代码重构就是一条绕不开的路。1.2 重构的目标边界重构最忌讳的是贪大求全什么都想重写一遍。很多项目翻车就是因为把重构当成了“顺手把以前看不顺眼的代码全部干掉”的机会结果范围失控半年都交付不了。我在项目启动第一天就和业务方拉齐了边界这次重构只替换系统本身数据要完整迁移规则要一比一还原历史数据不许丢同时预留未来扩展的余地。具体来说第一阶段的目标有五条完成架构选型和技术栈统一前后端代码全部纳入同一套工程管理体系。搭建可运行的前后端脚手架包含统一的接口规范、异常处理、日志埋点和环境配置。设计核心领域模型覆盖学员、课程、班级、报名、师资、教室等主数据以及后续章节会展开的排课、缴费、审批等业务数据。用Docker Compose把本地依赖环境MySQL、Redis一键拉起来保证新同学克隆仓库后十分钟能跑通前后端。打通一个最小可用的端到端链路前端登录页 → 后端鉴权接口 → 数据库落库 → 返回统一格式响应用来验证整个脚手架是通的。这些边界定下来之后后面所有选型都有了评判标准。比如某个技术选型如果会导致“新同学十分钟跑不起来”那它就要被淘汰某个中间件如果会让数据迁移更复杂它也要被淘汰。边界清晰了选型和实现才会快这比直接打开IDE开始写代码重要得多。2. 架构选型一次要管五年决策的取舍过程架构选型这件事我的习惯是先定大原则再逐个模块做决策。大原则有三个第一用主流稳定、社区活跃、招人容易的技术不追求冷门但先进的东西第二前后端各司其职通过标准HTTP接口通信不搞服务端渲染和前端混在一起的老式结构也不搞微服务这种对当前业务体量过度设计的东西第三所有组件尽量容器化开发环境和生产环境保持一致减少“我机器上能跑你机器上跑不了”的尴尬。从这套系统的实际体量看它就是典型的单体应用加若干可选中间件。用户量几位数并发峰值也就几十到上百单体应用加缓存、加索引优化完全够用。硬上微服务、上消息队列、上Kubernetes只会让运维复杂度和开发成本成倍上升而业务收益几乎为零。所以这一篇的结论是单体架构 模块化代码组织先把业务跑顺再说。2.1 前端Vue3 TypeScript Vite前端选型我在Vue和React之间权衡过。两个都能做但我最终选了Vue3 TypeScript Vite Element Plus这套组合理由很简单国内做管理后台的团队对Vue的熟悉度普遍更高Element Plus这类组件库对表格、表单、弹窗、树形控件这类管理端高频场景的覆盖非常完整能省掉大量造轮子的时间。而TypeScript不是可选项是必须项——业务系统里数据结构复杂学员信息、排课记录、审批单都是强类型对象用TypeScript能在编译期拦截掉大量低级错误。Vite作为构建工具是顺势而为。相比老一代的WebpackVite在开发环境下的冷启动速度和热更新速度都是碾压级的对于“改一行代码立刻看效果”的日常开发体验提升非常明显。生产构建方面Vite 5已经足够成熟打包Rollup的产物瘦身效果也不错。我见过一些团队还在用Vue2 Webpack的老组合不是说不能跑而是新项目实在没必要从三年前的配置开始折腾。关于组件库强调一点后端管理系统不要自己写组件。业务系统的核心价值在业务逻辑不在按钮样式和表格分页。Element Plus提供的能力覆盖了90%的管理端需求剩下10%再通过自定义组件封装。这样前端团队可以把精力放在报名流程、排课冲突检测、审批状态流转这些真正复杂的地方而不是天天调CSS。2.2 后端Spring Boot 3 MyBatis-Plus后端我选了Spring Boot 3 JDK 17 MyBatis-Plus。这个组合对国内业务系统开发者来说是舒适区但舒适不代表落后。Spring Boot 3是基于Spring Framework 6的版本底层切换到Jakarta EE规范内置了对JDK 17的完整支持在性能、可观测性、GraalVM原生镜像支持上都有明显提升。用Spring Boot不是为了赶时髦而是它把事务管理、依赖注入、Web框架、参数校验、统一异常处理这些基础能力都封装得非常成熟让团队能集中精力写业务代码。为什么不用Node.js或者Go倒不是技术上有硬伤而是从团队招聘成本和长期维护角度看Java在这类传统行业信息化项目里依然是综合成本最低的选择。MBA培训管理系统的核心复杂度在事务性业务逻辑比如报名锁定库存、缴费状态变更、退费审批流程这些场景用Spring的声明式事务管理会非常顺手。而Go更擅长高并发网络服务Node更擅长I/O密集型场景放到这个业务里都属于用牛刀杀鸡还增加了团队学习成本。MyBatis-Plus在ORM层面的定位也很精准。直接用MyBatis原生XML写简单CRUD很啰嗦用JPA/Hibernate复杂的多表关联查询和动态SQL又不好控制。MyBatis-Plus正好卡在中间单表操作自带通用Mapper复杂查询保留XML手写SQL的能力。这套系统后续会有大量报表类查询手写SQL反而更好优化这也是我放弃JPA的核心原因。需要注意如果选Spring Boot 3MyBatis-Plus要引入专门的启动器老版本的MyBatis-Plus对Spring Boot 3的兼容性是有问题的这个坑后面在踩坑章节我会展开说。2.3 数据库与缓存MySQL Redis数据层用了MySQL 8.0 Redis 7。MySQL没什么好争议的这类OLTP业务系统的标准答案。真正需要设计的是表结构。低代码平台把数据都存在一个大动态表里查起来要靠JSON解析性能极差。这次重构要做的是把这些数据“拆开摊平”恢复成规范的二维表结构。比如学员基础信息一个表课程一个表班级一个表报名关系一个表缴费流水一个表每个表都有清晰的主外键和索引。表结构设计是下一篇的重点这里先搭好数据库连接和初始化机制。Redis在这里不是装饰品。我规划了三个核心用途一是验证码和登录态的存储登录Token放Redis里可以控制过期时间也支持主动踢人下线二是报名热数据的缓存比如课程剩余名额、热门班的实时报名人数这类数据在报名季会被高频读取直接查数据库会给MySQL带来不必要的压力三是分布式锁虽然单体应用不一定要用分布式锁但后续如果“确认报名”和“缴费回调”同时在多实例部署下执行Redis锁是成本最低的兜底方案。这套系统里Redis和MySQL的分工很明确MySQL是数据真相的唯一来源所有关键业务操作都以MySQL事务为准Redis只做性能和临时状态的加速层缓存丢了可以从MySQL重建。这个原则必须从一开始就定下来否则后面很容易出现缓存和数据库数据不一致的脏数据问题。2.4 开发环境的容器化开发环境用Docker Compose统一管理MySQL和Redis这是我强烈推荐的做法。以前没有容器化的时候新同事入职要先装MySQL、配账号密码、建库、导数据光环境准备就能折腾半天。现在我在仓库根目录放一个docker-compose.yml里面定义好MySQL 8.0和Redis 7两个服务指定好端口、密码、数据卷执行一条docker compose up -d环境就绪。新同学克隆代码后十分钟跑通靠的就是这套机制。有人会担心数据卷的问题。我专门给MySQL挂了命名卷这样容器删了重建数据还在不会因为重启容器导致开发数据丢失。同时我预留了一个sql/init目录放初始化建库建表脚本MySQL容器首次启动时会自动执行这样第二次克隆仓库的人不需要手动导入SQL文件。这套自动化流程的收益是长期的尤其是团队有好几个开发者并行开发时保证大家的环境一致能省掉大量“我这边没问题啊”的扯皮。3. 脚手架搭建实操从空目录到能跑起来选型定了接下来就是动真格。我会按照我实际操作的顺序把完整搭建过程拆开讲。这里要说明下面的所有步骤都是我在这个项目里实践过的代码片段可以直接复制改改就能用但更重要的是理解每一步为什么这么做这样才能在遇到新问题时举一反三。3.1 整体目录规划我先在GitHub上建了一个仓库名字就叫mba-training-system然后规划了如下的目录结构frontend/Vue3 TS Vite前端工程backend/Spring Boot后端工程docker/放置Docker Compose编排文件和初始化SQL脚本docs/放架构文档、接口规范、会议记录这个结构不搞复杂的Monorepo工具链就用最朴素的文件夹划分。有人会推荐用Nx或者Turborepo管理多包仓库但对这种只有前后端两个工程、又没有大量共享代码的规模引入额外工具纯属增加复杂度。我更倾向于保持简单前后端各自独立构建、独立部署只通过接口契约通信。仓库根目录放一个README.md把项目简介、环境要求、快速启动指南、目录说明写清楚。这个README是给下一个接手的人看的也是给三个月后的自己看的写详细一点非常值得。我会在快速启动部分写清楚三个命令docker compose up -d拉环境、启动后端、启动前端新人照着做就能跑起来。3.2 后端工程初始化后端工程我直接用Spring Initializr生成的骨架。在Spring Initializr里我选择的依赖包括Spring Web、Validation、MySQL Driver、Lombok。JDK版本选17构建工具选Maven。生成之后解压到backend目录然后手动往pom.xml里补MyBatis-Plus和相关依赖。补完的pom.xml关键部分类似这样parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version relativePath/ /parent properties java.version17/java.version mybatis-plus.version3.5.6/mybatis-plus.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version${mybatis-plus.version}/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies注意这个细节Spring Boot 3下MyBatis-Plus的artifactId是mybatis-plus-spring-boot3-starter不是老的mybatis-plus-boot-starter。我用的是3.5.6版本它对Spring Boot 3.2的支持是稳定的。如果团队还在用老版本建议先升级再迁移不然会碰到一堆兼容性报错。然后我写了工程的基础配置。application.yml里把端口、应用名、MySQL连接、Redis连接配好同时拆出多环境配置文件。开发环境的配置我单独放到application-dev.yml里生产环境的配置会在后续部署章节补充现在先留一个占位文件。spring: application: name: mba-training-backend datasource: url: jdbc:mysql://localhost:3306/mba_training?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: mba_dev password: mba_dev_123 driver-class-name: com.mysql.cj.jdbc.Driver data: redis: host: localhost port: 6379 database: 0 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 server: port: 8080这里我把mybatis-plus的逻辑删除配置也顺手写上了deleted字段统一作为逻辑删除标记这是管理系统的常规操作避免物理删除导致历史数据无法追溯。接下来是后端的基础骨架代码我创建了如下包结构com.example.mba.controller接口入口层com.example.mba.service业务逻辑层com.example.mba.mapper数据访问层com.example.mba.entity实体类com.example.mba.common通用类包括统一返回体、全局异常处理器、常量定义统一返回体是我第一个写的类因为所有接口都要用它。类名叫Result泛型设计包含code、message、data三个字段配上几个静态工厂方法Data public class ResultT { private int code; private String message; private T data; public static T ResultT ok(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT fail(int code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }这个返回体是所有前后端联调的基础我把code设计成数字枚举200表示成功400表示参数错误401表示未认证403表示无权限500表示服务端异常。后续业务级的错误码我会放在常量类里统一管理。全局异常处理器用RestControllerAdvice注解实现捕获所有未处理异常转为统一返回体格式避免异常堆栈直接暴露给前端。为了验证脚手架通了我写了一个最简单的PingController提供GET /api/ping接口返回当前服务器时间和数据库连通状态。这个接口看着鸡肋但它是后续一切联调的第一步先确认网络通、框架通、数据库通再往下加业务模块就好排查问题了。3.3 前端工程初始化前端我用npm create vuelatest来初始化工程。这个脚手架会问你要不要TypeScript、Vue Router、Pinia等功能我全部选了Yes。生成后删掉自带的一些demo组件然后安装Element Plus、Axios等依赖。package.json里的核心依赖大致是这样的{ name: mba-training-frontend, version: 0.1.0, private: true, scripts: { dev: vite, build: vue-tsc --noEmit vite build, preview: vite preview }, dependencies: { vue: ^3.4.21, vue-router: ^4.3.0, pinia: ^2.1.7, element-plus: ^2.7.0, element-plus/icons-vue: ^2.3.1, axios: ^1.6.8 }, devDependencies: { vite: ^5.2.0, typescript: ^5.4.0, vue-tsc: ^2.0.0 } }前端工程的核心配置在vite.config.ts里。除了基础插件我重点配置了开发代理把所有以/api开头的请求转发到后端8080端口。这个配置的意义在于开发环境下前后端分离部署在不同端口但浏览器发起请求时不会存在跨域问题因为所有请求都发给了前端自己的5173端口由Vite在服务端转发。生产环境则由Nginx做同样的反向代理前后端部署模式保持一致。import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, host: true, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })前端的目录结构我做了基础规划src/api/按模块拆分的接口请求封装src/router/路由表src/stores/Pinia状态仓库src/views/页面组件src/layout/后台布局框架包含侧边栏和顶栏src/utils/工具函数和Axios实例封装Axios实例封装是前端最先要做的基建。我在utils/request.ts里创建了一个带拦截器的Axios实例请求拦截器负责从Pinia里取Token并加到请求头响应拦截器负责统一处理业务码遇到401自动跳登录页遇到500弹出错误提示。这样做的好处是所有接口调用都不需要重复处理错误逻辑页面里只要关心业务数据处理就行。登录页是这个项目的第一个页面。虽然完整的登录鉴权流程放到后面章节展开但脚手架阶段必须有一个能看的东西验证整条链路。我先做了一个简单的账号密码登录表单再加一个按钮调到后端ping接口点击后能看到数据库状态返回这个页面就成了前后端联调的“测试台”。3.4 打通前后端联调脚手架搭好不等于万事大吉端到端链路必须实际跑通一次。我按如下顺序做了验证第一步在项目根目录执行docker compose up -d启动MySQL和Redis容器确认两个服务都处于healthy状态。第二步在backend目录执行mvn spring-boot:run启动后端。启动日志里看到Tomcat started on port 8080说明Spring Boot起来了。接着用curl请求http://localhost:8080/api/ping返回了包含数据库状态的JSON说明MyBatis-Plus连接MySQL成功。第三步在frontend目录执行npm install然后npm run dev启动前端。浏览器访问http://localhost:5173打开登录页输入一个写死在数据库的测试账号登录后页面调用了后端的ping接口在页面上显示了“服务端连接正常”的提示。到这一步前端 → Vite代理 → 后端 → MySQL的完整链路就通了。这个验证过程看起来平淡但它是整个项目的地基。我见过很多项目脚手架阶段没验证端到端结果一开发业务模块就冒出各种环境问题最后查半天发现是根因在基础配置上。所以无论多简单我强烈建议在脚手架阶段就完成一次最小闭环验证。4. 踩坑记录与常见问题速查搭建过程中我踩了不少坑有些是版本兼容问题有些是配置细节问题。我整理了一个速查表按问题现象、根因、解决方案列出来方便遇到类似问题时快速对照。4.1 第一批次遇到的典型问题第一个坑是Spring Boot 3下的MyBatis-Plus依赖引入错误。一开始我图省事直接复制了网上老项目的MyBatis-Plus依赖artifactId是mybatis-plus-boot-starter结果启动直接报ClassNotFound。检查之后发现Spring Boot 3基于Jakarta EE规范老版本的MyBatis-Plus很多类还在javax包下完全不兼容。解决办法是换成mybatis-plus-spring-boot3-starter并且版本升到3.5.4以上。这个问题很典型网上很多教程还是基于Spring Boot 2写的做新项目的人容易被带偏。第二个坑是数据库字符集没有显式指定utf8mb4。我的开发机MySQL 8默认字符集其实已经是utf8mb4但为了稳妥我在创建的初始化SQL文件里显式加了CREATE DATABASE ... CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。这个动作看着多余但能避免未来上线时和测试库字符集不一致导致的乱码问题。尤其是学员姓名、地址这类用户输入内容万一碰上生僻字utf8mb3会直接报错或存成问号。第三个坑是Lombok和JDK 17的兼容问题。Lombok版本太老在JDK 17下编译会报annotation processing相关的错误。解决方法是把Lombok依赖版本升到1.18.30以上同时确保IDE的Annotation Processing选项是开启的。顺便说一句如果公司用的是Oracle JDK而不是OpenJDK建议同样保持JDK 17小版本更新到最新避开一些早期版本的已知bug。第四个坑是Vite代理没有生效。我配置了proxy之后半天没反应后来发现是因为前端请求的URL没有带/api前缀我调ping接口时直接写了/ping而不是/api/ping。等我统一了请求前缀后代理立刻生效。这个问题提醒我前后端接口路径规范从一开始就要定好我这边定的规则是所有业务接口统一以/api开头后端Controller的RequestMapping也统一带上/api前缀两边方向一致就不会出这种低级问题。4.2 第二批次中间件与数据问题第五个坑是Docker容器删除后数据全没。有次我为了调整端口映射直接删掉了MySQL容器再启动后发现库里刚建的测试表全没了。这是因为我没有挂数据卷。解决方式是在docker-compose.yml里为MySQL声明volumes把容器内的/var/lib/mysql映射到命名卷。改完之后我又把初始化SQL文件放进了docker-entrypoint-initdb.d目录这样每次首次启动都会自动建库建表既保证数据安全也保证环境可复制。第六个坑是Redis容器启动成功但后端连不上。排查后发现是docker-compose里Redis的端口映射写成了127.0.0.1:6379:6379后端连的是localhost但容器里的Redis绑定了容器内网IP按说这样应该能通。后来发现根因是我在application.yml里redis.database配了1而docker-compose里Redis服务没配对应的持久化和密码看起来能连上但验证时会失败。配置保持一致很重要我后来统一了端口、密码、数据库编号并把Redis也加了数据卷持久化避免缓存数据在容器重启后丢失。第七个坑是跨域预检请求被拦截。开发阶段我用Vite代理绕过了跨域但测试环境部署时由于前后端域名不同浏览器会先发OPTIONS预检请求。后端一开始没处理OPTIONS请求导致跨域失败。解决办法是在后端配置一个全局CORS过滤器允许指定来源、指定方法的跨域请求。但我的建议是如果用了Nginx做反向代理就尽量让前后端同源这样CORS配置都省了也会少很多麻烦。第八个坑是全局异常处理器和404不友好。Spring Boot默认对未匹配的接口返回Spring自带的错误页面而不是我们的统一Result结构。我要把所有异常都收敛成统一格式包括404、405、参数校验失败和业务异常否则前端解析响应时就得多写很多分支。最终我在全局异常处理器里补充了NoHandlerFoundException的捕获并配置了spring.mvc.throw-exception-if-no-handler-foundtrue。这一步做完前后端联调时遇到的响应格式问题就大幅减少了。这些坑基本覆盖了从零搭建一个全栈脚手架会遇到的高频问题。还有一个容易忽略的细节是.gitignore。前端要忽略node_modules和dist后端要忽略targetIDE配置文件和本地环境变量文件也要忽略。尤其是application-dev.yml里含数据库密码不能提交到仓库我在仓库里放的是application-dev.example.yml真实配置由开发者在本地复制一份并自行填写。这个习惯可以避免真实账号密码泄露到代码仓库里。5. 脚手架阶段的下一步规划脚手架跑通后我没有立刻开始写业务代码而是花了两天时间把一些基础工作补齐。第一件事是把接口文档规范定下来我选择了OpenAPI 3.0规范后端集成了springdoc-openapi依赖启动后访问/swagger-ui.html就能看到每个接口的定义和参数说明。前端团队可以依据这个文档并行开发不用后端写完一个接口就口头通知一遍。第二件事是补齐了前端的基础布局框架。后台管理系统的布局一般就是侧边栏菜单加顶部导航加内容区我基于Element Plus的Container组件搭了一套菜单项根据路由表动态生成。这套布局是后续所有页面的容器先把架子搭好后面往里填页面就快了。第三件事是搭建了日志规范和基础埋点。后端集成了SLF4J Logback统一日志格式区分了请求日志、业务日志和错误日志前端在Axios响应拦截器里加了一层简单的请求耗时统计。上线之后这些基础的可观测性数据能帮我们快速定位问题现在不做后面补更麻烦。第四件事是准备了一套最小可用的初始化数据。我把课程分类、学期、校区地址、常用字典项这些基础数据写成了SQL脚本跟着容器启动时自动导入。有了这些基础数据后续开发报名页面、排课页面时就不会因为缺数据而卡住联调。到这里架构选型与全栈脚手架搭建这一篇的内容就基本完成了。我个人在整个过程中的最大体会是选型和搭架子看着不产生业务价值但它决定了后面开发是顺流而下还是逆水行舟。尤其是从低代码平台迁移到纯代码架构的项目前期把技术边界、数据方案、环境自动化想清楚后面每一步都会轻松很多。下一篇我会写核心领域模型设计重点拆解学员、课程、班级、报名、缴费这几张核心表的表结构设计和ER关系还会聊一聊设计规范为什么执行得越早越省事。感兴趣的话可以关注这个系列的更新。