Description
Operating System
N/A
Browser Version
N/A
Firebase SDK Version
N/A
Firebase SDK Product:
Firestore
Describe your project's tooling
N/A
Describe the problem
Feature request
Would it be possible to provide an API for begginning/commiting a transaction without having to perform all the transaction operantions within a single closure?
Also it would be extremely helpful to not have to use another set of functions to manipulate the data when using a transaction and when not. When inside a transaction, operations have to be done through the transaction
object (transaction.set
) and when there is no transaction they are done through the firestore modular functions (setDoc
).
Problem description
The fact that firebase only provides the runTransaction()
api is very limiting for architecturing an app using different architectural patterns and abstracting functionality as the application scales.
For instance
In the case of implementing a service/repository pattern, where the service handles all the business logic and the repositories encapsulates all the database queries, the issue is clearly visible when I need to start a transaction from the service and call multiple repositories involved in the operation.
Now I'm forced to:
- Create a new layer of abstraction for sharing the
transaction
object between the service and the repositories. - Inject this new layer in all my services for initializing transactions.
- Inject this new layer in all my repositories for accessing the
transaction
object in each operation. - Duplicate the code in each operation for checking if I'm inside a transaction or not.
- This is because the code is different depending if there is a transaction or not, and the repositories should still work event if they are not inside a transaction.
- For example
setDoc
vstransaction.set
Code example
// TransactionManager
import { runTransaction as fsRunTransaction } from 'firebase/firestore'
let openTransaction: Transaction | null = null
const transactionManager = {
const db = getFirestore(getFirebaseApp())
const runTransaction = (updateFn) => {
return fsRunTransaction(
db,
async (transaction) => {
// Keep a reference to the open transaction so I can make different operations across different repositories.
openTransaction = transaction
await updateFn(transaction)
openTransaction = null
}
)
}
return { transaction: openTransaction, runTransaction }
// Service
const create = async (snapshot) => {
transactionManager.runTransaction(async () => {
const account = await accountsRepository.get(snapshot.accountId)
if (!account) throw new Error('Account not found')
await snapshotsRepository.create(snapshot)
await accountsRepository.update({ ...account, balance: snapshot.balance })
})
}
// Repository
const create = async (snapshot) => {
const snapshotsRef = collection(db, 'accounts', snapshot.accountId, 'snapshots')
const snapshotRef = doc(snapshotsRef)
if (transactionManager.transaction) {
transactionManager.transaction.set(snapshotRef, snapshot)
} else {
await setDoc(snapshotRef, snapshot)
}
}
Steps and code to reproduce issue
N/A