六边形、洋葱、策略、适配器架构设计
场景说明
一家提供“行业级数据/行为分析与算法服务”的平台,多个公司(Company A, B, C)接入。各公司数据采集、字段、格式与接入协议可能不同(HTTP webhook、SFTP、消息队列、第三方API等),但核心算法引擎(例如:行为得分、异常检测、ROI 计算、聚合指标计算)是公用的。平台需要:
- 对不同公司做接入适配(Adapter)
- 以算法策略选择不同分析方法(Strategy)
- 使用六边形架构把核心业务逻辑(Domain)与外部接口(DB、消息、API)解耦
- 使用洋葱架构分层组织依赖反转(Domain 最内层,不依赖外层)
- 提供后台长期运行任务(worker、调度、重试、任务状态持久化)
概念详解
六边形架构(Ports & Adapters / Hexagonal)
- 核心理念:系统应围绕域逻辑(Domain / Application)构建,通过“端口(ports)”定义对外接口(入站/出站),外部系统通过“适配器(adapters)”实现这些端口。
- 优点:实现了依赖方向从外向内,方便替换外部实现(数据库、消息、第三方),利于测试(可以注入 mock 适配器)。
- 在本系统的映射:
- Ports:
Repository
、MessageBroker
、DataIngestor
、AlgorithmRunner
的接口。 - Adapters:MySQL 实现/Redis 实现/HTTP webhook 适配器/文件处理适配器等。
- Ports:
洋葱架构(Onion)
- 核心理念:应用被分成多层,内层包含 Domain(实体、聚合、业务规则),外层依赖内层,内层不依赖外层。应用服务、接口层、持久层按圈层组织。
- 与六边形的关系:这两者高度兼容 — 洋葱强调层次、六边形强调端口/适配器。我们可以把 Domain 放在洋葱最内层,Ports/Interfaces 作为内外层交互契约,Adapters 在最外层实现契约。
六边形 vs 洋葱 —— 是否重复?
- 结论:二者互补,不是互斥。六边形强调“端口/适配器”的概念,洋葱强调“层次/依赖方向”。在实际工程中,常把 Domain 放在洋葱内层,同时用六边形的 ports 作为内外契约 —— 所以可以同时使用,且常常是最佳实践组合。
策略模式(Strategy)
- 核心理念:定义一系列可替换的算法(策略),将它们封装起来,使得它们可以互换。客户端通过统一接口选择并使用某个策略。
- 在本系统:不同公司或场景需要不同分析算法(例如:轻量得分/深度模型/历史加权),实现为多个
Strategy
,并运行时根据租户配置、数据特性或 A/B 测试动态选择。
适配器模式(Adapter)
- 核心理念:把一个类的接口转换成客户端所期望的另一个接口,解决接口不兼容问题。
- 在本系统:各公司数据格式与接入方式不同,编写适配器把公司专有的数据映射为平台内部统一的
IngestEvent
或NormalizedRecord
,使核心算法不必处理各种原始格式。
项目架构
analysis-platform/ # 项目根
├── cmd/
│ └── api/ # main for HTTP API
│ └── main.go
├── internal/
│ ├── domain/ # 洋葱最内层:实体、值对象、领域服务接口
│ │ ├── model.go
│ │ └── algorithm.go # Algorithm (Domain-level interface)
│ ├── application/ # 应用服务层(use cases)
│ │ ├── service.go
│ │ └── workflow.go
│ ├── ports/ # 六边形的 ports(接口定义)
│ │ ├── repository.go
│ │ ├── messagebroker.go
│ │ └── ingestor.go
│ ├── adapters/ # 外围适配器(外层)
│ │ ├── db/
│ │ │ └── mysql_repo.go
│ │ ├── mq/
│ │ │ └── kafka_adapter.go
│ │ └── ingesters/
│ │ ├── http_webhook.go
│ │ └── sftp_ingest.go
│ └── strategies/ # 策略实现(具体算法)
│ ├── strategy_light.go
│ └── strategy_advanced.go
├── scripts/
├── configs/
└── docs/
设计理念(要点)
- Domain 最小依赖:Domain 层只包含纯业务概念和接口(接口抽象在 ports),不导入外部库。
- 接口优先 / 依赖注入:应用层通过接口(ports)与外部交互,启动时由 main 注入具体 adapters 实现。
- 策略解耦算法:算法以策略(Strategy)实现并注入,便于扩展/AB 测试/运行时切换。
- 适配器隔离接入复杂性:各公司接入变化只影响 adapters,不影响核心逻辑。
- 可测试性:所有关键点(仓库、消息、算法)都可替换为 mock。
- 可观测性与重试:工作流与后台任务应支持任务追踪、重试、幂等、持久化任务状态。
- 多租户支持:每个请求/任务携带
tenant_id
,算法/策略可以基于 tenant 配置做变更。
后台工作流设计
- 任务种类:实时分析(同步/近实时)、批量重算(离线)、模型训练(长期)、聚合任务(定期)。
- 队列:使用 Redis Streams / Kafka / RabbitMQ 进行异步解耦。任务入队后由 worker 池消费。
- Worker 特性:
- 并发控制、幂等检查(唯一 task_id)、重试(指数退避)、死信队列/告警。
- 任务元数据表(task_id、tenant、type、payload、status、attempts、last_error、created_at)。
- 调度:cron 或基于事件触发入队。
- 监控:队列长度、失败率、处理延时(Prometheus + Grafana)。
- 持久化:每次任务成功/失败更新数据库(便于审计与重试)。