1️⃣ AIChats 完整数据流
先看最终数据流:
Network
↓
DTO / NetworkModel (Codable)
↓
Cache (Disk / Memory / TTL)
↓
Domain Model (struct, Sendable)
↓
Interactor / Presenter
↓
SwiftUI View State
↓
Persistence Entity (@Model class)■ 每一层职责
① NetworkModel(DTO)
特点:
Codable
与接口强绑定
不参与业务逻辑
不直接进入 UI
struct ChatMessageDTO: Codable {
let id: String
let content: String
}② Cache 层
解决:
首屏速度
离线恢复
streaming snapshot
TTL 过期策略
你之前的本地优先策略正是这一层。
③ Domain Model(核心)
这是整个架构的中心。
特点:
struct
Sendable
Hashable
可被 UI 安全持有
不依赖存储框架
struct ChatMessage: Identifiable, Sendable, Hashable, Codable {
var id: String
var content: String
}④ Interactor / Presenter
职责:
网络 + 缓存协调
业务逻辑
streaming 管理
optimistic UI
这里不应该出现 SwiftData Entity。
⑤ UI 层
SwiftUI 只消费 Domain。
避免:
SwiftData 泄漏
引用语义污染
并发风险
⑥ Persistence(SwiftData)
只负责:
查询
存储
关系
change tracking
2️⃣ SwiftUI + VIPER 中 Model 的最佳分层
这是你现在 AIChats 最推荐结构:
Modules/Chat/
├── DTO/
├── Domain/
├── Entity/
├── Mapper/
├── Repository/
├── Interactor/
├── Presenter/
└── View/■ DTO
只做接口映射。
■ Domain
真正业务数据。
Interactor / Presenter / UI 全部使用。
■ Entity
SwiftData / CoreData / Realm 绑定。
永远不要直接给 UI。
■ Mapper
DTO ↔ Domain
Entity ↔ Domain
■ Repository
整合:
Network
Cache
Persistence
输出 Domain。
3️⃣ 如何避免 SwiftData 污染 Domain(关键)
这是很多项目后期崩溃的原因。
❌ 错误做法
UI 直接持有:
@Query var messages: [ChatMessageEntity]问题:
引用语义污染 UI
actor 不安全
diff 不稳定
测试困难
数据库替换困难
✅ 正确做法
Repository 输出 Domain:
func fetchMessages() async -> [ChatMessage]Interactor / Presenter 持有 Domain。
UI 只绑定 Domain。
■ Mapper 是关键隔离层
extension ChatMessageEntity {
func toDomain() -> ChatMessage {
ChatMessage(id: id, content: content)
}
}4️⃣ 为什么这种架构对 AIChats 特别重要
你的项目具有:
streaming message
optimistic UI
本地优先缓存
AI token streaming
timeline UI
并发任务很多
这些都要求:
👉 值语义稳定
👉 UI snapshot 稳定
👉 actor 安全
SwiftData Entity 无法满足。
5️⃣ struct + class 的黄金职责划分
struct
DTO
Domain
Cache snapshot
View state
class / actor
Service
Manager
Store
Router
SwiftData Entity
6️⃣ 未来扩展能力(双模型的最大价值)
采用该架构后:
可替换 SwiftData
可引入 CloudKit
可做 event sourcing
可做 timeline snapshot
可做 message replay
可做离线 AI cache
对 AIChats 非常关键。
⭐ 最终总结
数据流
Network → Cache → Domain → UI → Persistence
分层原则
DTO / Domain / Entity 三层隔离
SwiftUI 原则
UI 永远只持有 struct Domain
SwiftData 原则
Entity 仅用于存储,不进入业务与 UI
如果你愿意
我可以继续帮你写下一篇进阶(强烈建议):
👉 AIChats Repository 设计(本地优先 + streaming + diff)
👉 AsyncStream 在数据层的最佳用法
👉 如何让 Presenter 不持有 SwiftData 也能实时更新 UI
👉 SwiftData + AsyncStream 的组合方案(非常高级)
这一套会直接让你的 AIChats 架构到达可产品化级别。