Firestore与Flutter原子事务实现:如何避免代码逻辑重复
解决Firestore事务中代码重复与单一职责的冲突问题
核心解决方案:封装可复用的事务操作单元
要在保证事务原子性的前提下避免代码重复、维持函数单一职责,关键是把F1和F2的核心逻辑拆分为独立的事务操作单元,再在需要组合的场景中,在同一个事务内依次调用这些单元。
1. 拆分独立的事务操作函数
将创建客户、创建客户列表的核心逻辑抽离成只专注于单一场景的函数,这些函数接收Firestore事务对象作为参数,只负责执行对应的数据库操作:
// 事务操作单元:仅负责在事务中创建客户 function createCustomerInTransaction(transaction, employeeId, customerData) { const customerRef = db.collection('customers').doc(); transaction.set(customerRef, { ...customerData, employeeId, createdAt: new Date() }); return customerRef; } // 事务操作单元:仅负责在事务中创建客户列表(用employeeId作为文档ID,天然满足C1规则) function createCustomerListInTransaction(transaction, employeeId) { const listRef = db.collection('customerLists').doc(employeeId); transaction.set(listRef, { employeeId, customerIds: [], createdAt: new Date() }); return listRef; }
2. 构建对外业务接口
基于上述操作单元,分别实现F1和F2的对外接口,在需要组合操作的场景中,将多个单元放入同一个事务执行:
// 对外接口F1:添加客户(处理C2、C3场景) async function createCustomer(employeeId, customerData) { return db.runTransaction(async (transaction) => { const listRef = db.collection('customerLists').doc(employeeId); const listDoc = await transaction.get(listRef); // C3场景:无客户列表时先创建列表 if (!listDoc.exists) { createCustomerListInTransaction(transaction, employeeId); } // 执行创建客户逻辑 return createCustomerInTransaction(transaction, employeeId, customerData); }); } // 对外接口F2:单独创建客户列表(满足单独调用场景) async function createCustomerList(employeeId) { return db.runTransaction(async (transaction) => { const listRef = db.collection('customerLists').doc(employeeId); const listDoc = await transaction.get(listRef); if (listDoc.exists) { throw new Error("员工已拥有客户列表"); // 符合C1规则 } return createCustomerListInTransaction(transaction, employeeId); }); }
方案优势
- 无代码重复:创建客户列表的逻辑只在
createCustomerListInTransaction中实现一次,F1和F2都复用该逻辑 - 单一职责:每个函数只负责自己的核心业务,事务操作单元专注于数据库操作,对外接口专注于业务规则判断
- 原子性保障:所有组合操作都在同一个Firestore事务中执行,满足C4的原子性要求,要么全部成功,要么无任何数据写入
这是否是NoSQL的局限?
这不是NoSQL的局限,而是代码组织设计的问题。关系型数据库中,为了保证跨表操作的原子性,同样需要将多个操作放入同一个事务,此时也会遇到类似的代码复用需求——通过封装存储过程、公用DAO方法等方式解决,本质思路和上述方案一致。
Firestore的事务模型基于文档级原子操作,只要将独立业务逻辑封装为可复用的事务单元,就能在同一事务中灵活组合调用,既满足原子性要求,又能维持代码的可维护性。
内容的提问来源于stack exchange,提问作者Matheus
相关产品推荐
相关产品推荐

