# NATS Subject 强类型重构设计文档

## 1. 背景与现状 (Background & Status Quo)

目前，项目中微服务之间的 NATS 消息通信主要依赖于硬编码的字符串（String Literals）来指定 Subject（主题）。例如：
- `services/report-generator-service` 使用 `"events.analysis.report_generated"` 发布消息。
- `services/workflow-orchestrator-service` 使用 `"events.analysis.>"` 订阅消息，并使用字符串匹配 `if subject == "events.analysis.report_generated"` 来区分消息类型。

这种方式存在以下问题：
1.  **弱类型约束**：字符串拼接容易出现拼写错误（Typos），且无法在编译期捕获，只能在运行时发现，违反了 "Fail Early" 原则。
2.  **维护困难**：Subject 散落在各个服务的代码中，缺乏统一视图（Single Source of Truth），修改一个 Subject 需要全局搜索并小心替换。
3.  **缺乏契约**：Subject 与 Payload（消息体）之间的对应关系仅通过注释或隐式约定存在，缺乏代码层面的强制约束。

## 2. 目的 (Objectives)

本设计旨在贯彻 Rustic 的工程原则（强类型约束、单一来源、早失败、无回退），通过以下方式重构 NATS Subject 的管理：

1.  **强类型枚举 (Enum-driven Subjects)**：在 `common-contracts` 中定义全局唯一的枚举类型，涵盖系统中所有合法的 NATS Subject。
2.  **消除魔法字符串**：禁止在业务逻辑中直接使用字符串字面量进行 publish 或 subscribe 操作。
3.  **编译期安全**：利用 Rust 的类型系统，确保 Subject 的构造和匹配是合法的。

## 3. 设计方案 (Design Proposal)

### 3.1 核心数据结构 (`common-contracts`)

在 `services/common-contracts/src/subjects.rs` 中定义 `NatsSubject` 枚举。该枚举涵盖系统中所有合法的 NATS Subject。

```rust
use uuid::Uuid;
use std::fmt;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NatsSubject {
    // --- Commands ---
    WorkflowCommandStart,       // "workflow.commands.start"
    WorkflowCommandSyncState,   // "workflow.commands.sync_state"
    DataFetchCommands,          // "data_fetch_commands"
    AnalysisCommandGenerateReport, // "analysis.commands.generate_report"

    // --- Events ---
    // Analysis Events
    AnalysisReportGenerated,    // "events.analysis.report_generated"
    AnalysisReportFailed,       // "events.analysis.report_failed"
    
    // Data Events
    DataFinancialsPersisted,    // "events.data.financials_persisted"
    DataFetchFailed,            // "events.data.fetch_failed"

    // Workflow Events (Dynamic)
    WorkflowProgress(Uuid),     // "events.workflow.{uuid}"

    // --- Wildcards (For Subscription) ---
    AnalysisEventsWildcard,     // "events.analysis.>"
    WorkflowCommandsWildcard,   // "workflow.commands.>"
    DataEventsWildcard,         // "events.data.>"
}

// ... impl Display and FromStr ...
```

### 3.2 使用方式

#### 发布消息 (Publish)

```rust
// Old
state.nats.publish("events.analysis.report_generated", payload).await?;

// New
use common_contracts::subjects::NatsSubject;

state.nats.publish(NatsSubject::AnalysisReportGenerated.to_string(), payload).await?;
```

#### 订阅与匹配 (Subscribe & Match)

```rust
// Old
let sub = nats.subscribe("events.analysis.>").await?;
while let Some(msg) = sub.next().await {
    if msg.subject == "events.analysis.report_generated" { ... }
}

// New
let sub = nats.subscribe(NatsSubject::AnalysisEventsWildcard.to_string()).await?;
while let Some(msg) = sub.next().await {
    // 将接收到的 subject 字符串尝试转换为枚举
    match NatsSubject::try_from(msg.subject.as_str()) {
        Ok(NatsSubject::AnalysisReportGenerated) => {
            // Handle report generated
        },
        Ok(NatsSubject::AnalysisReportFailed) => {
            // Handle report failed
        },
        _ => {
            // Log warning or ignore
        }
    }
}
```

## 4. 实施状态 (Implementation Status)

### 4.1 `common-contracts`
- [x] 定义 `NatsSubject` 枚举及相关 Trait (`Display`, `FromStr`) 在 `src/subjects.rs`。
- [x] 添加单元测试确保 Round-trip 正确性。

### 4.2 `report-generator-service`
- [x] `src/worker.rs`: 替换 Publish Subject。

### 4.3 `workflow-orchestrator-service`
- [x] `src/message_consumer.rs`: 替换 Subscribe Subject 和 Match 逻辑。

### 4.4 `api-gateway`
- [x] `src/api.rs`: 替换 Publish Subject。

### 4.5 Provider Services
- [x] `finnhub-provider-service`: 替换 Subscribe Subject，移除魔法字符串常量。
- [x] `alphavantage-provider-service`: 替换 Subscribe Subject，移除魔法字符串常量。
- [x] `tushare-provider-service`: 替换 Subscribe Subject，移除魔法字符串常量。
- [x] `yfinance-provider-service`: 替换 Subscribe Subject，移除魔法字符串常量。

## 5. 进阶优化 (Future Work)

- [x] **关联 Payload 类型**: 利用 Rust 的 trait 系统，将 Subject 枚举与对应的 Payload 结构体关联起来，使得 `publish` 函数能够根据 Subject 自动推断 Payload 类型，从而防止 Subject 与 Payload 不匹配的问题。
  ```rust
  trait SubjectMessage {
      // type Payload: Serialize + DeserializeOwned; // Simplified: trait is implemented on Payload struct itself
      fn subject(&self) -> NatsSubject;
  }
  ```
  已在 `services/common-contracts/src/subjects.rs` 中实现 `SubjectMessage` trait，并在 `messages.rs` 中为各个 Command/Event 实现了该 trait。各服务已更新为使用 `msg.subject().to_string()` 进行发布。
