# 架构决策记录（ADR 汇总）

> 本文件记录 lingniu-vehicle-ingest v2 的关键架构决策，后续每次重大调整追加条目（不删除旧条目）。

## ADR-001  消息通道：Kafka
- **Status**: Accepted
- **Context**: 需要高吞吐、严格单车有序、成熟生态
- **Decision**: 使用 Kafka，分区 key = VIN
- **Consequences**: 下游消费者需使用消费组 + 分区内顺序消费；生产链路只支持 Kafka。

## ADR-002  线上消息格式：Protobuf
- **Status**: Accepted
- **Context**: 需要向前兼容、体积小、性能好
- **Decision**: 线上用 Protobuf，调试/诊断保留 JSON 序列化能力
- **Consequences**: 需维护 `.proto` schema，`ingest-api` 模块负责生成 Java stub

## ADR-003  command-gateway 独立模块
- **Status**: Accepted
- **Context**: 下行命令链路不应与接入进程耦合
- **Decision**: 拆出独立 `command-gateway` 模块，复用 `session-core`；本次重构同步交付
- **Consequences**: HTTP 对外接口（原 JT808Controller / JT1078Controller）迁移到此模块

## ADR-004  信达 Push：Legacy only
- **Status**: Superseded by ADR-011
- **Context**: 旧实现硬编码凭证、无重连、0300/0401 空实现
- **Decision**:
  1. 新模块 `inbound-xinda-push` 隔离第三方库 `gps-push-client`
  2. 配置外置（yml + env）
  3. 自动重连 + Resilience4j 熔断
  4. 0200/0300/0401 **仅做协议解析**，转换为 `VehicleEvent` 后直接投 Kafka
  5. **业务处理全部下沉到下游消费者**，本服务不再实现报警/透传业务逻辑
- **Consequences**: 信达 Push 现在不属于默认生产面；模块仅保留在 `legacy-xinda` profile，后续优化优先投向 GB32960、JT808、宇通 MQTT、历史和统计链路。

## ADR-005  JT1078 / JSATL12 进第一批
- **Status**: Superseded by ADR-012
- **Decision**: 第一批迁移即覆盖 JT1078 信令 + JSATL12 报警附件上传
- **Consequences**: Phase 2 工期相应拉长；JSATL12 需要对象存储后端（本地 / S3 / OSS）

## ADR-006  部署形态：GB32960 三应用拆分
- **Status**: Superseded
- **Context**: 原 `bootstrap-all` 一体化启动已被 GB32960 拆分部署替代
- **Decision**: 生产默认部署 `gb32960-ingest-app`、`vehicle-history-app`、`vehicle-analytics-app` 三个应用；旧 `bootstrap-all` 模块删除
- **Consequences**: CI/CD 分别构建三个镜像；协议接入、历史查询、统计消费可以独立发布和回滚

## ADR-007  Java 25 + 虚拟线程 + Disruptor
- **Status**: Accepted
- **Decision**:
  - Netty EventLoop 只做解码与 RingBuffer 投递，严禁阻塞
  - 业务 Handler 走虚拟线程（`Thread.ofVirtual()`）
  - 协议间背压通过 Disruptor RingBuffer 表达
  - 禁用 `synchronized`，使用 `ReentrantLock` 避免虚拟线程 pinning
- **Consequences**: 需开启 `jdk.VirtualThreadPinned` JFR 监控

## ADR-008  协议接入应用不直接写业务库
- **Status**: Accepted
- **Decision**: GB32960、JT808、Yutong MQTT 等协议接入应用只负责收、解析、校验、规整和投递 Kafka，不直接写业务表。
- **Consequences**: 业务落库由独立 Kafka 消费应用承接：`vehicle-history-app` 写入 TDengine 历史与 RAW JSON，`vehicle-analytics-app` 写入 MySQL `vehicle_stat_metric`。

## ADR-009  构建工具：Maven
- **Status**: Accepted
- **Decision**: 沿用 Maven，统一 BOM 管理版本，Spotless 格式化，ArchUnit 守护分层

## ADR-010  框架：Spring Boot 3.5
- **Status**: Accepted
- **Decision**: Spring Boot 3.5.x，支持 JDK 25；AutoConfiguration 用于按需启停

## ADR-011  默认生产面：三协议接入 + 历史 + 统计
- **Status**: Accepted
- **Context**: 生产接入范围已经从 GB32960 三应用拆分，扩展为三条活跃接入链路和两个消费应用；信达 Push 已废弃，不应再出现在默认构建、部署或优化目标里。
- **Decision**:
  1. 默认生产面包含 GB32960、JT808、Yutong MQTT、vehicle-history-app、vehicle-analytics-app。
  2. Xinda Push 仅保留在 `legacy-xinda` profile，不进入默认 Maven reactor、Woodpecker 镜像发布和历史消费绑定。
  3. `command-gateway` 和 JT1078 继续作为可选能力，通过 `optional-command-gateway` profile 显式启用。
- **Consequences**: 默认构建和部署保持精简；后续性能、可靠性、字段解析和存储优化都优先服务三条活跃协议链路。

## ADR-012  JSATL12 附件上传：Optional only
- **Status**: Accepted
- **Context**: 当前默认生产面只部署 GB32960、JT808、Yutong MQTT、vehicle-history-app、vehicle-analytics-app；JSATL12 附件上传没有独立生产 app，也不在 Portainer 和 Woodpecker 活跃镜像列表中。
- **Decision**:
  1. `protocol-jsatl12` 不进入默认 Maven reactor。
  2. JSATL12 仅保留在 `optional-attachments` profile，需要附件上传能力时显式构建。
  3. 默认优化和验证优先覆盖活跃接入链路，附件上传能力保持源码可用但不增加默认构建面。
- **Consequences**: 默认构建更轻，生产部署边界更清晰；附件上传相关测试需要通过 `-Poptional-attachments` 显式运行。

## ADR-013  最新状态服务：Optional only
- **Status**: Accepted
- **Context**: Redis 最新状态查询是独立消费能力，但当前默认 Portainer 部署和 Woodpecker 镜像发布只包含三条接入链路、history 和 analytics；`vehicle-state-service` 没有独立 app，也不应增加默认构建面。
- **Decision**:
  1. `vehicle-state-service` 不进入默认 Maven reactor。
  2. vehicle-state-service 仅保留在 `optional-latest-state` profile，需要 Redis 最新状态查询能力时显式构建。
  3. 默认优化和验证优先覆盖活跃接入、TDengine 历史和 MySQL 指标链路。
- **Consequences**: 默认构建和部署边界继续收窄；最新状态能力保持源码可用，但其测试需要通过 `-Poptional-latest-state` 显式运行。

## ADR-014  raw-archive-store 原型已删除
- **Status**: Accepted
- **Context**: 默认生产 raw bytes 写入由 `sink-archive` 负责，历史查询通过 TDengine `raw_frames` 和 `archive://...` 引用追溯；`raw-archive-store` 只是未部署的独立读写原型，继续保留会造成 raw archive 路径歧义。
- **Decision**:
  1. 删除 `raw-archive-store` 模块和对应 optional profile。
  2. 默认生产链路只保留 `sink-archive`、Kafka raw topic、TDengine raw_frames 这条 raw 路径。
  3. 如需新的 archive store 能力，先以生产链路需求重新设计，不恢复旧原型。
- **Consequences**: 默认构建面和可选构建面继续收窄；raw bytes 写入职责集中在 `sink-archive`。

## ADR-015  文件型事件索引：Removed
- **Status**: Accepted
- **Context**: 默认历史查询已收敛到 TDengine `raw_frames`、`vehicle_locations` 和按需解码；旧文件型索引会增加一套无生产部署的查询和依赖边界。
- **Decision**:
  1. 删除旧文件型事件索引模块和 profile。
  2. 父 POM 不再管理旧索引驱动依赖。
  3. 历史查询和 RAW 回放统一以 TDengine + `archive://...` 引用为准。
- **Consequences**: 默认构建面和可选构建面都更小；需要历史查询时只维护 TDengine 一条路径。
