2. MES系统架构设计:单体架构 vs 微服务架构、半导体MES的典型分层架构、接口设计原则(API First)

好,咱们直接进入正题。MES系统架构怎么选,这问题我几乎每次跟新团队合作都会被问到。说白了,没有银弹。但半导体行业有它自己的脾气,选错了架构,后面改起来真是要命。

2.1 单体架构 vs 微服务架构:别跟风,看场景

先聊聊单体架构。我最早做的那套MES,就是典型的单体应用。一个WAR包丢到WebLogic上,所有功能——派工、报表、物料追踪——全在一个进程里跑。

单体架构的特点:

  • 开发初期快,一个团队就能搞定
  • 调试方便,IDE里直接跑
  • 部署简单,一个包搞定

但问题也明显。我记得有一次,报表模块的一个慢查询把整个应用的数据库连接池占满了,结果产线上的派工请求全堵在那。操作员急得跳脚,我这边只能重启应用。嗯,这就是单体架构的痛——模块之间没有隔离。

那微服务呢?听起来很美。每个服务独立部署、独立扩缩容。但我要泼点冷水:半导体MES不适合一上来就搞微服务。

为什么?

  • 半导体产线对事务一致性要求极高。一个晶圆从光刻到刻蚀,中间状态不能乱。微服务的分布式事务,处理起来非常头疼。
  • 网络延迟。微服务之间走RPC调用,一次操作可能要串起五六个服务。产线上的设备等不起那几毫秒。
  • 运维成本。你想想看,一个几十个微服务的系统,光监控、日志、链路追踪就得搭一套基础设施。小团队根本玩不转。

我个人习惯的做法是:先单体,后拆分。初期用模块化单体,把业务边界划清楚。等业务复杂度上来了,再把那些确实需要独立扩展的模块(比如报表、数据分析)拆成微服务。我在项目中遇到过,有个客户非要一步到位上微服务,结果半年了还在调服务间的接口,产线迟迟上不了线。

维度 单体架构 微服务架构
开发效率 初期高 初期低,后期高
事务一致性 强,本地事务 弱,需补偿机制
部署运维 简单 复杂
适用场景 中小产线、快速上线 大型工厂、多产线异构

2.2 半导体MES的典型分层架构

不管选单体还是微服务,分层架构的思想是不变的。我画了一张图,你看一眼就明白了。

半导体MES典型分层架构 第1层:设备接口层 (EAP/Equipment Integration) SECS/GEM, HSMS, PLC通信, 设备状态采集 第2层:核心业务层 (Core Business) 派工调度 | 物料追踪 | 工艺参数管理 | 质量检测 批次管理 | 返工处理 | 数据采集(SPC) 第3层:数据服务层 (Data Services) 实时数据库 | 历史数据归档 | 报表引擎 | 数据仓库 第4层:应用展示层 (Presentation) Web客户端 | 移动端 | 看板大屏 | 报表门户 数据流向 核心关注层

这张图我建议你保存下来。每一层都有它的职责,别让业务逻辑渗透到设备接口层去。我曾经见过一个项目,在EAP层里直接写了业务判断——「如果产品是A型号,就跳过某步检测」。结果后来工艺变了,改代码改到崩溃。

我的建议:

核心业务层是MES的心脏。这一层要做得足够「薄」——只处理业务规则,不关心数据怎么存、界面怎么展示。这样以后换数据库、换前端框架,你只需要动对应层,心脏不动。

2.3 接口设计原则:API First

接口设计这块,我吃过不少亏。早期做MES,接口都是「顺手写的」——前端要什么字段,我就给什么字段。结果后来第三方系统要对接,发现接口字段名不统一、返回格式乱七八糟。

所以我现在坚持一个原则:API First。先定义接口,再写代码。

具体怎么做?我一般分三步:

  1. 先写接口文档。用OpenAPI规范(Swagger)把每个接口的请求、响应、错误码定义清楚。团队评审通过再动手。
  2. 接口契约即代码。用工具生成接口的桩代码,前后端并行开发。前端不用等后端,后端也不用担心接口变。
  3. 版本管理。接口一旦发布,尽量向前兼容。实在要改,就加版本号,比如/api/v2/

来看一个实际的接口定义示例。这是半导体MES中查询批次信息的接口:

# 查询批次信息 API
GET /api/v1/lots/{lotId}

# 请求参数
#   lotId: 批次号,字符串,必填

# 响应示例 (JSON)
{
  "lotId": "LOT-202405-001",
  "productName": "CMOS-28nm",
  "currentStep": "光刻-01",
  "status": "IN_PROCESS",
  "waferCount": 25,
  "holdFlag": false,
  "createdTime": "2024-05-20T08:30:00Z"
}

# 错误码
# 200: 成功
# 404: 批次不存在
# 500: 服务器内部错误

你看,接口定义里连错误码都写清楚了。前端拿到这个文档,直接就能开始写页面。后端照着这个实现,两边对一下就能联调。

避坑指南:

我曾经遇到一个项目,接口文档和代码严重不一致。文档说返回字段叫waferCount,代码里实际返回的是wafer_qty。前端排查了半天,最后发现是后端改了字段名没更新文档。所以我现在要求:接口文档必须通过自动化测试验证——每次构建都跑一遍契约测试,文档和代码不一致就构建失败。

还有一个细节:接口的粒度。半导体MES里,一个操作往往涉及多个步骤。比如「开始生产」这个动作,可能要更新批次状态、记录设备开始时间、扣减物料。我建议把这些操作封装成一个业务接口,而不是让前端调三个接口。这样既保证事务一致性,也减少网络开销。

总结一下接口设计要点:

  • 先定义,后实现(API First)
  • 接口文档即契约,自动化验证
  • 版本管理,向前兼容
  • 粗粒度接口,减少调用次数
  • 统一错误码规范

嗯,架构设计这块就先聊到这。记住一句话:架构是为业务服务的,不是用来炫技的。选单体还是微服务,分几层,接口怎么设计,都要回到「产线能不能稳定跑起来」这个根本问题上。


专注资料整理