4. 平台总体架构设计

生物实验数据与计算协同平台要同时面对三类差异极大的负载:仪器原始数据的持续写入、分析计算任务的突发调度、以及科研人员对结果的高频查询与追溯。如果把这些能力揉进一个单体应用,任何一次测序仪批量导入都可能拖慢全平台的查询响应。因此总体架构设计的第一原则是按变化频率与资源特征分层,第二原则是按业务能力边界拆分微服务,第三原则是把数据流与控制流分开规划——数据走大吞吐通道,控制走低延迟通道,二者不互相阻塞。

4.1 分层架构:五层职责与依赖方向

平台采用五层结构,自上而下为接入层、应用服务层、领域服务层、数据访问层与基础设施层。依赖方向严格单向向下,上层只能调用下层暴露的接口,禁止跨层反向调用,也禁止同层横向直连数据库。

  • 接入层:负责协议适配与安全边界,包括 Web 前端、REST/GraphQL 网关、仪器数据上报的 gRPC 接口、以及大文件分片上传端点。网关统一完成 JWT 校验、限流(默认单用户 50 QPS)与请求路由。
  • 应用服务层:编排用例,不含领域规则。例如“提交一次差异表达分析”这一用例,由该层依次调用样本服务校验样本状态、调用计算服务创建任务、调用通知服务推送进度。
  • 领域服务层:承载核心业务规则,如样本状态机、实验批次合法性校验、分析流程版本锁定、数据权限判定。这一层是平台最稳定、最值得投入测试的部分。
  • 数据访问层:封装存储差异,对上层提供仓储接口。关系型数据走 PostgreSQL,原始仪器文件走对象存储,分析中间结果走列式存储,检索走 OpenSearch。
  • 基础设施层:容器编排、消息队列、缓存、日志与指标采集,不含业务语义。

4.2 微服务与模块化:拆分粒度与边界

微服务拆分过细会带来分布式事务与链路追踪成本,过粗则失去独立伸缩能力。本平台按“业务能力 + 数据所有权”双维度拆分,得到六个核心服务:样本与实验管理服务、仪器数据接入服务、计算流程编排服务、结果与报表服务、权限与审计服务、通知服务。每个服务独占自己的数据库 schema,其他服务只能通过 API 或事件读取,不得直连其库表。

模块化在服务内部同样重要。以计算流程编排服务为例,内部划分为流程定义模块、任务调度模块、资源配额模块与执行器适配模块。执行器适配模块通过统一接口对接 Nextflow、Snakemake 与自研 Python 执行器,新增一种执行引擎只需实现该接口,不影响调度逻辑。这种“服务间松耦合、服务内高内聚”的结构,使平台在新增单细胞分析流程时,只需扩展流程定义与执行器适配,无需改动样本与权限服务。

拆分判据:若两个功能的数据写入频率相差一个数量级,或伸缩需求明显不同(如接入服务需横向扩展到 20 副本,权限服务 2 副本足够),就应拆为独立服务。

4.3 数据流与控制流规划

数据流描述“字节如何移动”,控制流描述“决策如何下达”。二者必须分离,否则大文件传输会阻塞任务状态更新。

数据流路径:测序仪产出 FASTQ 后,由接入服务计算分片校验和,经分片上传写入对象存储的原始区(raw bucket),同时向消息队列投递一条 raw.data.arrived 事件,事件体只含对象路径、样本 ID、校验和与字节数,不含文件内容。计算服务消费该事件后,从对象存储拉取数据到计算节点本地盘,产出中间结果写回对象存储的中间区(interim bucket),最终结果写入结果区(result bucket)并登记元数据。

控制流路径:用户在前端点击“开始分析”,请求经网关到达应用服务层,应用服务调用计算服务创建任务,计算服务写入任务记录(状态 PENDING)并投递调度消息。调度器根据资源配额决定是否立即执行,状态依次流转为 QUEUED → RUNNING → SUCCEEDED/FAILED。每次状态变更都通过事件总线广播,通知服务据此推送进度,审计服务据此落库。

下面这张图给出平台总体架构与两条流的分工:左侧为控制流(细线、低延迟、经网关与领域服务),右侧为数据流(粗线、大吞吐、经对象存储与消息队列),二者在计算服务处交汇但不互相阻塞。

生物实验数据与计算协同平台 · 总体架构与数据/控制流 接入层 Web / 移动前端 API 网关 · JWT · 限流 gRPC 仪器上报 分片上传端点 应用服务层(用例编排) 样本与实验管理 计算流程编排 结果与报表 领域服务层(业务规则) 样本状态机 流程版本锁定 数据权限判定 审计与配额 数据访问层(仓储接口) PostgreSQL 对象存储 列式存储 OpenSearch 基础设施层 容器编排 消息队列 缓存 / 指标 日志采集 控制流 数据流 事件总线:raw.data.arrived 仅传路径 / 样本ID / 校验和 / 字节数 任务状态流转 PENDING→QUEUED→RUNNING→SUCCEEDED 存储分区 raw → interim → result

4.4 关键设计约束与落地检查

架构设计必须落到可检查的约束上,否则容易在迭代中退化。以下约束在代码评审与部署流水线中强制执行:

约束项具体要求验证方式
依赖方向上层只能调用下层接口,禁止跨层反向调用静态依赖分析,CI 中失败即阻断合并
数据所有权每个服务独占 schema,禁止跨库直连数据库账号权限隔离 + 连接串审计
事件体大小消息队列事件体不超过 64 KB,只传引用不传内容生产者端序列化后长度校验
控制流延迟任务状态变更到前端可见不超过 2 秒端到端埋点,P95 监控告警
数据流吞吐单样本 200 GB FASTQ 上传不阻塞其他用户查询压测:并发上传 5 路时查询 P95 < 500 ms

以一个具体场景收束:某实验室一次导入 12 个样本、共 1.8 TB FASTQ。接入服务并行分片上传,每片 64 MB,校验和逐片写入元数据;上传完成后投递 12 条 raw.data.arrived 事件。计算服务按配额(默认每用户 4 个并发任务)将 12 个任务排入队列,前 4 个进入 RUNNING,其余保持 QUEUED。用户此时刷新样本列表,查询走的是 PostgreSQL 与缓存,不受对象存储写入影响,响应仍在 300 ms 以内。当某个任务失败时,控制流将其置为 FAILED 并携带错误码(如 EXIT_137 表示内存超限),通知服务推送告警,用户可调整内存配额后重跑,而无需重新上传原始数据——这正是数据流与控制流分离带来的直接收益。

常见误区:把文件内容直接塞进消息队列,或让计算服务直接读取其他服务的数据库。前者会让队列积压并拖垮调度,后者会在服务拆分后产生难以排查的隐式耦合。