# 契约中心后端架构

## 目的

以纯 TypeScript `Core` 作为后端权威契约。它比传统 DDD 的 Domain Layer 更宽：既包含业务
语义，也包含约束持久化和基础设施实现的技术无关契约。因此应称为契约中心或六边形 Core，
而不是严格的传统 DDD；仍然可以在 Model 中使用 DDD 建模方法。

## 分层职责

### Core

- Model：业务身份、状态、生命周期和语言。
- Record Contract：持久化读写形状，包括 SQL 可空性与序列化值。
- Mapper：Model 与 Record Contract 之间的纯转换。
- Repository Contract：应用所需的持久化行为。
- Use Case Contract：暴露给调用者的应用操作。
- Port：应用需要的外部能力。
- Rule：没有 I/O 的确定性业务或协议判断。

Core 必须能在不启动数据库、Redis、HTTP Server、Worker 或外部服务的情况下使用。

### Database

- ORM Table Schema 与 Relation。
- SQL 查询和 Repository 实现。
- 事务、连接池、migration、索引和数据库约束。
- 证明 ORM 查询 Record 与 Core Record Contract 一致的编译期检查。

不要在 Database 重新定义 Model、Record Contract 或纯 Mapper。

### Infra

- Redis、加密、AI Provider、对象存储、队列、时钟等 Port 的实现。
- SDK 适配、连接生命周期、重试策略、可观测性和 Provider 专属错误。

不要让 SDK 类型泄漏到 Core。

### API Contract

- 使用普通 TypeScript `interface` 定义的 JSON Request、Response 与 DTO。
- 与 interface 并列、命名明确的 `validateXxx()` 运行时验证函数。
- HTTP 错误、分页等公开协议结构。
- 构建期从 interface 与 JSDoc 约束生成的 OpenAPI JSON。

API Contract 不导出 Core Model 或 Database Record，也不依赖 Hono。日期使用 ISO 8601 字符串，
认证身份由 Server 注入，不能信任客户端提交的 userId。

### Shared

- 前后端确实共用、确定性且无 I/O 的纯函数。
- 不依赖 DOM、Node.js、数据库、HTTP 框架或环境变量的错误收集、字段检查等 Validator
  基础能力。

Shared 不是杂项目录；只被单侧使用的辅助函数应留在所属包内。

### Server 或 Application 组合层

- HTTP 路由和传输 DTO 转换。
- 认证编排和请求上下文。
- 依赖构造与注入。
- 项目将接口和实现分离时的 Use Case 实现或协调。

## 类型归属判断

依次询问：

1. 即使存储技术改变，该概念是否仍表达业务身份或生命周期？放入 Model。
2. 它是否定义实现必须保存或返回的稳定形状？放入 Record Contract。
3. 它是否是两个 Core 表示之间的确定性转换？放入 Mapper。
4. Core 是否需要某个外部行为，但不关心如何实现？定义 Port。
5. 它是否是具体 SQL、Redis、SDK、网络、文件系统或加密机制？放入 Database 或 Infra。
6. 它是否定义前后端共享的 JSON 请求、响应、校验或公开错误？放入 API Contract。
7. 它是否是前后端确实共用且与协议无关的纯函数？放入 Shared。
8. 它是否只涉及 Hono Context、Header 读取或状态码写入？放在 Server 边界。

| 概念 | 归属 | 原因 |
| --- | --- | --- |
| Topic | Model + Record | 具有持久身份的长期业务记忆 |
| Message | Model + Record | 不可变的采集事实 |
| SQL 可空 Message 行 | Record Contract | Message 的持久化表示 |
| Model/Record 空值转换 | Mapper | 纯且共享的契约转换 |
| Redis replay key | Infra | 短期实现状态 |
| replay reservation 行为 | Port | Core 只需要该能力 |
| Drizzle Table | Database | PostgreSQL 实现细节 |
| Hono Context 中的 Body 读取 | Server | 框架专属请求适配 |
| JSON Request/Response interface | API Contract | 前后端共享的公开传输契约 |
| `validateXxx()` | API Contract | 对应 interface 的显式运行时校验 |
| Hono Validator middleware | Server | 框架适配与响应编排 |
| 字段检查与错误收集函数 | Shared | 前后端共用的确定性纯函数 |

## Model 与 Record 的关系

即使两者结构接近，也应分开，因为变化原因不同：

- Model 随业务含义变化。
- Record 随持久化兼容性、可空性、序列化、索引和 migration 变化。

优先使用 `IMessage`、`MessageRecord`、`messageMapper` 等明确名称。Record 中的 SQL `NULL`
和 Model 中的可选缺失应有意转换。不要在 Core 中把 Record 别名为 ORM `$inferSelect`。

## 依赖规则

允许：

```text
database → core
infra    → core
server   → core + database + infra + api-contract + shared
api-contract → shared
frontend → api-contract + shared
```

禁止：

```text
core → database
core → infra
core → server
core → api-contract
api-contract → core
shared → core + api-contract + server
```

同时检查包依赖与源码 import。仅类型 import 也属于架构耦合，必须遵守相同方向。

## 命名规则

中心包包含业务 Model、Record、Mapper、Repository、Use Case 和 Port，因此统一命名为
`core`。`contracts` 通常过窄，会弱化 Model 与纯规则；`domain` 容易让人误认为这是严格的
传统 DDD Domain Layer。

## 审查清单

- Core 是否独立于框架和 SDK？
- Model 与 Record 的命名和职责是否分离？
- Mapper 是否确定性且无 I/O？
- Database 与 Infra 实现是否都指向 Core Contract？
- 短期实现状态是否留在业务 Model 之外，除非它确实具有审计或生命周期语义？
- 当可空性或值类型漂移时，Schema 兼容检查是否会在编译期失败？
- 只有持久化结构实际变化时才新增 migration 吗？
- 是否运行了受影响的 type check、build、test 与 migration 检查？
