3、OA系统架构解析:前后端分离架构、微服务架构在OA中的应用、数据库设计原则(MySQL/PostgreSQL)
好,咱们进入第三章。这一章我打算聊聊OA系统的架构设计。说实话,很多做OA二次开发的同行,上来就改代码,结果改着改着发现系统越来越臃肿,维护成本直线上升。为什么?因为没搞懂架构。
我个人习惯,在动手改任何代码之前,先花半天时间把架构图捋一遍。你想想看,一栋楼要加层,你总得先看看承重墙在哪吧?OA系统也是一样的道理。
3.1 前后端分离架构:为什么是标配?
早些年做OA,JSP里嵌Java代码,前端后端搅在一起。改个按钮样式,得把整个项目重新编译部署。我在2016年接手过一个老项目,那叫一个痛苦——前端同事改个CSS,还得找我拉分支、重启Tomcat。
前后端分离之后,情况完全变了。前端只管界面和交互,后端专注业务逻辑和数据。两者通过HTTP/HTTPS接口通信,数据格式一般用JSON。
核心要点:
- 前端:Vue.js / React / Angular,负责路由、状态管理、UI渲染
- 后端:Spring Boot / Node.js / Go,提供RESTful API
- 通信:JSON over HTTP,Token鉴权(JWT常用)
举个例子,一个审批流程的提交操作:
// 前端调用(Vue示例)
axios.post('/api/approval/submit', {
processId: 'P20240301',
formData: { applicant: '张三', amount: 5000 }
}).then(res => {
// 处理返回结果
})
// 后端接口(Spring Boot示例)
@PostMapping("/api/approval/submit")
public Result submitApproval(@RequestBody ApprovalDTO dto) {
// 校验、存储、触发流程
return Result.success();
}
这样做的好处很明显:前端和后端可以并行开发,互不干扰。我见过一个团队,前端用Vue,后端用Python Flask,配合得挺好。说白了,只要接口约定好了,用什么技术栈都行。
我的经验:接口文档一定要用Swagger或YApi维护。我曾经因为接口字段名没对齐,线上出了个bug——前端传的是"userId",后端认的是"user_id",查了一下午。
3.2 微服务架构在OA中的应用
小公司OA,单体架构够用。但一旦用户量上来,比如几千人同时在线审批、考勤打卡,单体应用就容易扛不住。这时候,微服务就派上用场了。
微服务说白了,就是把一个大OA拆成多个小服务。每个服务独立部署、独立扩展。比如:
- 用户服务:管理组织架构、角色权限
- 流程服务:处理审批流、表单引擎
- 考勤服务:打卡、排班、统计
- 消息服务:通知、待办提醒
- 文件服务:附件上传、在线预览
我参与过一个OA改造项目,原来单体应用一到月底考勤统计就卡死。拆成微服务后,考勤服务单独部署了3个节点,问题就解决了。
微服务架构的关键组件:
- 服务注册与发现:Nacos / Eureka
- API网关:Spring Cloud Gateway / Kong
- 配置中心:Nacos / Apollo
- 远程调用:Feign / Dubbo
- 链路追踪:SkyWalking / Zipkin
不过我得提醒一句:微服务不是银弹。如果你的OA只有几十个人用,强行上微服务反而增加复杂度。我曾经见过一个团队,5个人的项目搞了8个微服务,结果部署、调试、排错的时间比写代码还多。
避坑指南:我曾经在一个项目中,因为服务间调用链路过长,导致一个审批提交操作耗时3秒。后来发现是每个服务都单独查了一次数据库。解决方案是引入缓存(Redis)和批量查询接口。
3.3 数据库设计原则(MySQL/PostgreSQL)
数据库设计,是OA二次开发的根基。很多问题,追根溯源都是表结构没设计好。我见过最夸张的一个OA系统,一张流程表单表有200多个字段,查询一次要5秒。
下面是我总结的几个核心原则:
3.3.1 范式化与反范式化的平衡
OA系统里,用户表、部门表、角色表这些基础数据,一定要遵循第三范式。但像流程表单、审批记录这种高频查询的场景,适当反范式化反而更好。
举个例子:
-- 范式化设计(标准)
CREATE TABLE approval_record (
id BIGINT PRIMARY KEY,
process_id VARCHAR(32),
applicant_id BIGINT,
approver_id BIGINT,
create_time DATETIME
);
-- 反范式化设计(查询更快)
CREATE TABLE approval_record_denormalized (
id BIGINT PRIMARY KEY,
process_id VARCHAR(32),
applicant_id BIGINT,
applicant_name VARCHAR(50), -- 冗余字段
approver_id BIGINT,
approver_name VARCHAR(50), -- 冗余字段
create_time DATETIME
);
为什么这么做?因为OA里经常要展示「申请人姓名」和「审批人姓名」。如果每次都要JOIN用户表,数据量大了性能就下来了。冗余字段虽然占点空间,但换来了查询速度。
3.3.2 索引设计:别乱加,也别不加
我见过有人给每个字段都加了索引,结果插入一条数据要半天。也见过一张表几百万数据,连个索引都没有。
正确的做法是:
- 主键索引:必须的,一般用自增ID或雪花算法
- 唯一索引:业务上需要唯一约束的字段,比如流程编号
- 普通索引:经常出现在WHERE、JOIN、ORDER BY后面的字段
- 联合索引:多个字段组合查询时,注意最左前缀原则
我的习惯:用EXPLAIN分析慢查询。我曾经优化过一个考勤统计接口,加了一个联合索引后,查询时间从2.3秒降到了0.05秒。
3.3.3 MySQL vs PostgreSQL:怎么选?
| 对比项 | MySQL | PostgreSQL |
|---|---|---|
| 流行度 | 极高,生态完善 | 较高,近年增长快 |
| 事务支持 | InnoDB支持ACID | 原生支持,更强大 |
| JSON支持 | 有,但功能有限 | 非常强大,支持索引 |
| 全文检索 | 有,但较弱 | 内置,性能好 |
| 适合场景 | 传统OA、简单查询 | 复杂报表、JSON存储 |
我个人建议:如果OA系统里有很多动态表单、自定义字段,PostgreSQL的JSONB类型会非常方便。如果团队对MySQL更熟悉,用MySQL也完全没问题,关键是把索引和SQL写好。
3.3.4 分表分库:什么时候做?
别一上来就分表分库。我见过一个项目,用户才500人,就搞了32个分表,结果查询还要路由,开发效率极低。
一般来说,单表数据超过500万行,或者单表写入TPS超过1000,才需要考虑分表。分库的话,通常是业务模块之间耦合度低,比如把用户库和流程库分开。
避坑指南:我曾经在一个OA项目里,因为没做分表,流程日志表到了800万行后,插入和查询都变得很慢。后来用按月份分表解决了。记住:分表策略一定要提前设计好,别等出问题了再改。
3.4 小结
这一章我们聊了前后端分离、微服务、数据库设计。说白了,架构设计就是做取舍——在灵活性、性能、维护成本之间找平衡点。没有完美的架构,只有适合当前阶段的架构。
下一章,我会讲讲OA系统里最核心的模块——流程引擎的设计与二次开发。嗯,那个才是真正考验功力的地方。