第2章:系统架构设计——多能协同控制软件的总体架构、分层设计思想、核心模块划分
各位同学,咱们今天聊聊系统架构。说实话,我见过太多项目一开始就急着写代码,结果做到一半发现模块之间耦合得跟麻花似的,改一个地方崩一片。多能协同控制软件尤其如此——它要管电、热、冷、气多种能源,还要实时调度,架构要是没搭好,后面就是无底洞。
我个人习惯,做架构设计前先问自己三个问题:系统要解决什么问题?数据怎么流动?模块之间怎么解耦?想清楚了再动手。下面我就把这些年踩过的坑和总结的经验,掰开了讲给你听。
2.1 总体架构:三层两总线
先看一张我手绘的架构图,这是咱们课程的核心骨架。
这张图我称之为「三层两总线」架构。为什么这么画?我在项目里吃过亏——早期版本把数据访问和业务逻辑混在一起,结果换一个数据库类型,改了整整两周。后来痛定思痛,强制分层,世界清净了。
核心原则:上层依赖下层,下层绝不反向依赖上层。每一层只通过总线与相邻层通信。
2.2 分层设计思想:为什么非分不可?
你想想看,一个多能协同系统里,可能有光伏、储能、燃气轮机、热泵、冷机……每种设备的通信协议、数据格式都不一样。如果不分层,代码会变成什么样?
我见过一个真实案例:某项目把设备驱动、业务逻辑、界面渲染全写在一个类里,3000多行。后来要新增一种储能设备,开发人员改了三天,改出了五个bug。这就是典型的「大泥球」架构。
分层的好处,说白了就三点:
- 隔离变化:换设备驱动不用动调度算法,换数据库不用动界面
- 便于测试:每一层都可以单独写单元测试,我习惯先测服务层,再测数据层
- 团队协作:前端团队、算法团队、数据团队各管一层,互不干扰
嗯,这里要注意:分层不是越多越好。我见过有人分了七层,结果一个简单的数据查询要穿越五六个接口,性能惨不忍睹。咱们这个系统,三层刚刚好。
2.3 核心模块划分:各司其职
下面我把每一层里的核心模块拆开讲。这些都是我在实际项目中反复验证过的。
2.3.1 数据层——系统的「记忆体」
数据层负责所有数据的存取。别小看它,多能协同系统的数据特点是:高频、多源、异构。
| 数据类别 | 存储方式 | 典型数据 | 更新频率 |
|---|---|---|---|
| 实时数据 | 内存数据库(Redis) | 电压、电流、功率、温度 | 毫秒级 |
| 历史数据 | 时序数据库(InfluxDB) | 日发电量、负荷曲线 | 秒级 |
| 模型数据 | 关系数据库(PostgreSQL) | 设备参数、拓扑结构 | 低频更新 |
| 配置数据 | 文件系统 + 数据库 | 调度策略、阈值设置 | 按需更新 |
我曾经犯过一个错:把所有数据都塞进关系数据库,结果实时数据写入太频繁,把数据库搞崩了。后来改用时序数据库存高频数据,关系库只存配置和模型,问题迎刃而解。
我的建议:实时数据用Redis做缓存,历史数据用时序库,别混着用。这是血的教训换来的。
2.3.2 服务层——系统的「大脑」
服务层是核心中的核心。我把它拆成五个模块:
- 协同优化调度模块:这是灵魂。它根据负荷预测、电价信号、设备状态,算出最优的能源分配方案。说白了,就是决定「什么时候用光伏、什么时候用储能、什么时候买电」。
- 预测与态势感知模块:负责预测未来15分钟到24小时的负荷和新能源出力。我用过LSTM、Transformer,也用过传统的时间序列方法。没有银弹,得看数据特点。
- 安全校核模块:调度方案算出来之后,得检查会不会导致电压越限、线路过载。我见过一个项目没做安全校核,调度指令下发后变压器直接跳闸……
- 数据融合模块:不同设备的数据格式、时间戳可能不一致。这个模块负责清洗、对齐、插值。脏数据进,干净数据出。
- 规则引擎模块:有些场景不需要复杂优化,比如「光伏功率超过80%时,自动启动储能充电」。这种规则用规则引擎处理,比写死代码灵活得多。
注意:服务层模块之间不要直接调用。我建议通过消息队列异步通信。比如调度模块算完方案后,发一条消息给安全校核模块,校核通过后再发消息给执行模块。这样即使某个模块挂了,系统还能降级运行。
2.3.3 应用层——系统的「面孔」
应用层直接面对用户。我强调一个原则:应用层只做展示和交互,不做业务计算。
举个例子:用户点击「一键优化」按钮,应用层只负责把请求发给服务层,然后把结果展示出来。千万别在界面里写优化算法——我见过有人这么干,结果浏览器卡死了。
应用层我一般分这几个功能:
- 实时监控大屏:展示当前各能源系统的运行状态
- 调度策略配置:让用户设置优化目标(比如「最低成本」还是「最低碳排放」)
- 历史数据回放:查看过去某段时间的运行曲线
- 告警管理:显示异常事件,支持确认和处理
2.4 模块间通信:总线是关键
三层架构搭好了,模块之间怎么说话?我推荐用消息队列 + REST API的组合。
- 数据总线(消息队列):用于高频数据交换,比如实时数据采集、调度指令下发。我用过Kafka和RabbitMQ,小规模项目用RabbitMQ就够了。
- 服务总线(REST API):用于低频、请求-响应式的交互,比如用户查询历史数据、修改配置。
为什么不用一种方案包打天下?因为实时数据需要低延迟、高吞吐,而API调用需要可靠性和事务支持。各司其职,才是工程之道。
2.5 避坑指南:我踩过的三个坑
最后,分享几个我亲身经历过的教训:
- 坑一:一开始没做模块解耦,结果设备驱动升级时,影响了调度模块。后来我强制所有模块只通过总线通信,再也没出过类似问题。
- 坑二:数据层用了单一数据库,实时数据写入把历史查询拖慢了。解决方案是读写分离,实时库和历史库分开。
- 坑三:应用层直接调用了数据层的接口,绕过了服务层。结果业务逻辑散落在各处,维护成本飙升。后来我在代码审查里加了规则:应用层只能调服务层接口。
好了,系统架构这块就聊到这儿。记住一句话:架构设计不是一次性的,它需要随着业务演进不断调整。但分层、解耦、总线通信这几个原则,什么时候都不过时。
公众号:蓝海资料掘金营,微信deep3321