Swift中Service-Repository模式下Firebase与业务逻辑分离及原子删除
实现原子性操作并保持逻辑分离的方案
要在不耦合业务逻辑与Firebase事务代码的前提下实现三个操作的原子性,核心思路是将事务逻辑封装在数据层(Repository/协调器),业务层仅负责传递业务参数并触发原子执行流程,具体实现步骤如下:
1. 抽象Firebase事务接口
定义事务协议屏蔽Firestore原生Transaction细节,让业务层和Repository层无需直接依赖Firebase类型:
protocol FirestoreTransactionProtocol { /// 删除指定集合下的文档 func deleteDocument(from collection: String, documentID: String) throws /// 更新指定集合下的文档数据 func updateDocument(in collection: String, documentID: String, data: [String: Any]) throws } // 用Firestore原生Transaction实现上述协议 class FirestoreTransactionWrapper: FirestoreTransactionProtocol { private let transaction: Transaction init(transaction: Transaction) { self.transaction = transaction } func deleteDocument(from collection: String, documentID: String) throws { let docRef = Firestore.firestore().collection(collection).document(documentID) transaction.deleteDocument(docRef) } func updateDocument(in collection: String, documentID: String, data: [String: Any]) throws { let docRef = Firestore.firestore().collection(collection).document(documentID) try transaction.updateData(data, forDocument: docRef) } }
2. 给Repository添加事务兼容方法
为每个Repository新增支持事务的重载方法,保留原有非事务方法的同时提供原子操作入口:
TaskRepository
class TaskRepository { // 原有非事务删除方法 func deleteTask(from collection: String, taskID: String) async throws { try await Firestore.firestore().collection(collection).document(taskID).delete() } // 事务版本的删除方法 func deleteTask(transaction: FirestoreTransactionProtocol, from collection: String, taskID: String) throws { try transaction.deleteDocument(from: collection, documentID: taskID) } }
TeamRepository
class TeamRepository { // 原有非事务移除引用方法 func removeTaskReferenceFromTeam(field: String, teamID: String, taskID: String) async throws { try await Firestore.firestore().collection(FirestoreConstants.Collection.teams).document(teamID).updateData([ field: FieldValue.arrayRemove([taskID]) ]) } // 事务版本的移除引用方法 func removeTaskReferenceFromTeam(transaction: FirestoreTransactionProtocol, field: String, teamID: String, taskID: String) throws { try transaction.updateDocument( in: FirestoreConstants.Collection.teams, documentID: teamID, data: [field: FieldValue.arrayRemove([taskID])] ) } }
UserRepository
同理实现事务版本的移除方法:
class UserRepository { // 原有非事务方法 func removeTaskReferenceFromUser(field: String, userID: String, taskID: String) async throws { // 原有非事务实现 } // 事务版本方法 func removeTaskReferenceFromUser(transaction: FirestoreTransactionProtocol, field: String, userID: String, taskID: String) throws { try transaction.updateDocument( in: FirestoreConstants.Collection.users, documentID: userID, data: [field: FieldValue.arrayRemove([taskID])] ) } }
3. 实现事务协调器
专门负责执行Firebase事务,业务层通过它触发原子操作,无需接触Firebase事务细节:
class FirestoreTransactionCoordinator { private let db = Firestore.firestore() /// 执行原子事务操作,闭包内通过TransactionProtocol完成数据操作 func runTransaction<T>(_ operation: @escaping (FirestoreTransactionProtocol) throws -> T) async throws -> T { return try await db.runTransaction { transaction in let wrapper = FirestoreTransactionWrapper(transaction: transaction) return try operation(wrapper) } } }
4. 修改TaskService的删除逻辑
在Service层仅处理业务决策(判断任务状态对应集合和字段),然后通过协调器触发原子操作:
class TaskService { private let taskRepo: TaskRepository private let teamRepo: TeamRepository private let userRepo: UserRepository private let txCoordinator: FirestoreTransactionCoordinator // 通过依赖注入初始化各组件(便于测试和解耦) init(taskRepo: TaskRepository, teamRepo: TeamRepository, userRepo: UserRepository, txCoordinator: FirestoreTransactionCoordinator) { self.taskRepo = taskRepo self.teamRepo = teamRepo self.userRepo = userRepo self.txCoordinator = txCoordinator } func deleteTask(task: DistributionTask) async throws { // 业务逻辑:根据任务状态确定操作的集合和字段 let (tasksCollection, userTaskIdsField, teamTaskIdsField) = task.isCompleted ? (FirestoreConstants.Collection.completedTasks, FirestoreConstants.Field.User.completedTaskIds, FirestoreConstants.Field.Team.completedTaskIds) : (FirestoreConstants.Collection.activeTasks, FirestoreConstants.Field.User.activeTaskIds, FirestoreConstants.Field.Team.activeTaskIds) // 触发原子事务,所有操作要么全部成功,要么全部回滚 try await txCoordinator.runTransaction { transaction in try self.taskRepo.deleteTask(transaction: transaction, from: tasksCollection, taskID: task.id) try self.teamRepo.removeTaskReferenceFromTeam(transaction: transaction, field: teamTaskIdsField, teamID: task.teamId, taskID: task.id) try self.userRepo.removeTaskReferenceFromUser(transaction: transaction, field: userTaskIdsField, userID: task.distributorId, taskID: task.id) } } }
方案优势
- 完全解耦:业务层(TaskService)不包含任何Firebase事务代码,仅负责业务规则判断和流程触发;数据层(Repository/协调器)封装所有Firebase相关逻辑。
- 灵活性:每个Repository同时支持事务和非事务操作,适配不同业务场景。
- 可测试性:通过依赖注入,可轻松替换为Mock组件进行单元测试,无需依赖真实Firestore实例。
内容的提问来源于stack exchange,提问作者Antonio264656
相关产品推荐
相关产品推荐

