Swift整洁架构中多态实体(存在类型)的持久化方案问询
Swift整洁架构下
Source协议的持久化方案 核心矛盾拆解
你面临的问题本质是在整洁架构分层约束下,既要实现any Source类型的持久化(需保留类型信息以重构实例),又不能让领域层的Source协议依赖Codable这类持久化相关接口,避免污染核心领域逻辑。
最优实现:分层隔离的类型桥接方案
1. 领域层定义纯业务契约
在领域层的Source协议中新增两个抽象属性,仅聚焦业务需求(标识源类型、提供源配置),完全不涉及持久化细节:
protocol Source { func getData(id: String) -> Entity // 唯一标识当前Source类型(如"local_file"、"remote_api") var sourceTypeID: String { get } // 提供需要持久化的配置数据(如本地路径、API端点) var persistentConfig: [String: Any] { get } }
这两个属性属于领域逻辑的一部分(用户安装/卸载源本身就是业务需求),并非持久化侵入,完全符合整洁架构对领域层的要求。
2. 数据层实现类型映射与持久化
在数据层实现SourceRepository的具体逻辑,同时维护一个类型注册表,将sourceTypeID映射到对应的Source实例工厂。新增Source实现时,只需扩展注册表,无需修改Repository核心代码:
// 数据层的类型注册表:新增Source实现仅需在这里添加映射 private let sourceFactoryRegistry: [String: ([String: Any]) -> any Source] = [ "local_file": { config in LocalSource(storagePath: config["path"] as! String) }, "remote_api": { config in NetworkSource(apiURL: config["apiEndpoint"] as! String) } ] class CoreSourceRepository: SourceRepository { func saveInstalledSources(_ sources: [any Source]) throws { // 转换为可持久化的结构化数据 let persistedEntries = sources.map { source in [ "type": source.sourceTypeID, "config": source.persistentConfig ] } // 用任意持久化框架保存(示例用UserDefaults,可替换为CoreData/Realm等) UserDefaults.standard.set(persistedEntries, forKey: "user_installed_sources") } func loadInstalledSources() throws -> [any Source] { guard let persistedEntries = UserDefaults.standard.array(forKey: "user_installed_sources") as? [[String: Any]] else { return [] } // 通过注册表重构Source实例 return persistedEntries.compactMap { entry in guard let typeID = entry["type"] as? String, let config = entry["config"] as? [String: Any], let factory = sourceFactoryRegistry[typeID] else { // 跳过无法识别的源类型 return nil } return factory(config) } } }
3. 进阶:类型安全的配置处理
如果担心[String: Any]的类型不安全,可以在领域层给Source关联一个配置类型,让每个Source实现类定义自己强类型的配置模型,同时仅在数据层实现配置的序列化/反序列化:
// 领域层:关联配置类型的抽象协议 protocol Source { associatedtype Config func getData(id: String) -> Entity var sourceTypeID: String { get } var config: Config { get } init(config: Config) } // 数据层:给LocalSource添加Codable配置(仅数据层依赖Codable) extension LocalSource { struct StorageConfig: Codable { let storagePath: String } var config: StorageConfig { StorageConfig(storagePath: self.path) } init(config: StorageConfig) { self.path = config.storagePath } } // 数据层注册表调整为处理Codable配置 private let sourceFactoryRegistry: [String: (Data) -> any Source?] = [ "local_file": { data in guard let config = try? JSONDecoder().decode(LocalSource.StorageConfig.self, from: data) else { return nil } return LocalSource(config: config) } ]
这种方式既保证了领域层的纯净,又在数据层实现了类型安全的持久化。
关于Codable侵入的顾虑是否合理?
你的顾虑完全正确。整洁架构要求领域层是最稳定、最核心的层,不能依赖任何外部框架(包括Codable)。如果让Source直接继承Codable,会导致领域层与持久化逻辑强绑定,后续更换持久化方案(比如从UserDefaults切换到CoreData)时,领域层也需要修改,违反了依赖倒置原则。
总结
- 领域层仅定义业务相关的抽象契约,不涉及任何持久化细节
- 数据层通过类型注册表实现
any Source的实例重构,新增Source只需扩展注册表 - 完全隔离领域层与持久化逻辑,严格遵循整洁架构的分层规则
内容的提问来源于stack exchange,提问作者Cameron Delong
相关产品推荐
相关产品推荐

