You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:07:02