firebase / firebase/firebase-js-sdk

FR: API for making the transaction object globally accessible

Open
#8,083 3 comments 3 reactions 0 assignees View on GitHub
api: firestore type: feature request
Dominant language
TypeScript
Stars
5.1k
Forks
1k
Avg merge
2d 21h
Merged PRs (30d)
37

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

### Code example

```typescript
// 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 }

```

```typescript
// 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 })
})
}
```

```typescript
// 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

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.