Skip to content

FR: API for making the transaction object globally accessible #8083

Open
@sebagnz

Description

@sebagnz

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 vs transaction.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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions