Firebase Project Structure for Teams that are Growing: Scaling Financial Pipelines
The Architectural Debt of Early-Stage Firestore
When we started processing ₹100M/day, our Firestore structure was a monolith of collections. It worked for rapid prototyping, but as our volume scaled to ₹2B/day, the 'everything-in-one-place' approach became a liability. In fintech, architecture isn’t just about performance; it is about auditability, correctness, and preventing the catastrophic state of a desynchronized ledger. As your team grows and your throughput increases, you cannot afford to have data scattered across loosely coupled collections.
Scaling a Firestore project requires a transition from 'convenience-driven' data modeling to 'consistency-driven' structures. When you have multiple microservices interacting with the same user wallets, the Firestore project structure becomes the single source of truth for the entire business. A chaotic structure leads to race conditions, partial writes, and reconciliation nightmares. If your project structure doesn't support atomic transaction boundaries, you are not building a ledger; you are building a data dump that will eventually lose money.
Establishing Hierarchical Domain Isolation
To handle scale, you must group your data into logical domains. Do not flatten everything under the root level. For a growing fintech application, I advocate for a structure that segregates data by its lifecycle and volatility. In our high-throughput pipeline, we enforce a strict separation between 'Hot Data' (current wallet balances, pending transactions) and 'Cold Data' (archived transaction logs, immutable audit trails).
Consider the following structure:
# Project Structure Strategy
collections:
- wallets: { description: "User balances", index: "uid" }
- transactions: { description: "Ledger entries", index: "tx_id" }
- settlements: { description: "Daily batch reports", index: "date" }
- configs: { description: "Immutable system parameters" }
- locks: { description: "Distributed mutexes for atomic writes" }
By segregating 'locks' as a first-class collection, we ensure that our distributed systems can perform idempotent operations without stepping on each other's toes during high-load periods. Each service should have restricted access defined via Firebase Security Rules at the sub-collection level, ensuring that the ledger service cannot be touched by the notification or user-profile services.
Designing for Transactional Integrity and Concurrent Writes
In a system moving ₹2B daily, we treat every write as a financial record. Firestore transactions are our primary tool for maintaining integrity. When two concurrent requests attempt to update a user's wallet balance, the database must force an abort-and-retry cycle if the underlying data has changed. To scale this, your project structure must support small, atomic documents. Avoid 'fat' documents where a single document contains an entire user’s transaction history—this creates contention hotspots that will stall your write pipeline.
Here is how we implement a transaction-safe balance update in Kotlin:
suspend fun processTransaction(walletId: String, amount: Double) {
db.runTransaction { transaction ->
val walletRef = db.collection("wallets").document(walletId)
val wallet = transaction.get(walletRef)
val currentBalance = wallet.getDouble("balance") ?: 0.0
val newBalance = currentBalance + amount
// Reconciliation Check
if (newBalance < 0) throw IllegalStateException("Insufficient funds")
transaction.update(walletRef, "balance", newBalance)
transaction.set(db.collection("transactions").document(), mapOf(
"walletId" to walletId,
"amount" to amount,
"timestamp" to FieldValue.serverTimestamp()
))
}.await()
}
This pattern ensures that the 'ledger reconciliation' happens at the moment of the write. The key here is the transactions collection. By treating it as an immutable log, we ensure that every balance change is backed by an entry, providing an audit trail that is resilient to system failure.
The Role of Monitoring and Automated Reconciliation
As your team grows, developers will make mistakes. A bad deploy or an unhandled edge case in a cloud function can cause a discrepancy between the wallet balance and the transaction ledger. You cannot rely on human vigilance to catch these. Your project structure must include an automated reconciliation service that runs off-peak.
Our system runs a daily job that sums up all transactions for a given walletId and compares that to the balance stored in the wallets collection. If a discrepancy exists, the service alerts the SRE team and locks the wallet. This is why the project structure must facilitate easy aggregation. Using Firestore's aggregation queries or exporting to BigQuery via the Firestore-BigQuery extension is non-negotiable for large-scale operations. If you can't reconcile your data in under 30 minutes, you are effectively running a blind ledger.
Visualizing the Data Flow and Dependency Management
When scaling, the mental model of how data flows through the project is crucial. Imagine your Firestore project as a directed graph where data moves from entry points (client/gateway) into transactional buffers (collections) and eventually into archival storage.
[Gateway Service] -> [Pending Collection] -> [Ledger Transaction (Firestore)] -> [Audit/Archive]
By ensuring that every write passes through a 'ledgering service' rather than allowing client-side direct writes, you maintain control over the schema and validation logic. As teams grow, you will inevitably have multiple repositories. I recommend moving your data access layer into a shared internal library. This forces developers to use predefined, tested functions for reading and writing to Firestore, rather than writing raw db.collection().set() calls.
Standardizing the way we write to Firestore:
- Validation Layer: Sanitize inputs and enforce constraints.
- Transaction Layer: Execute the atomic operation using current balance snapshots.
- Post-Commit Hook: Trigger analytical events or downstream notifications only after the transaction is confirmed.
Eliminating Eventual Consistency Pitfalls
One of the biggest traps for teams moving from relational databases to Firestore is assuming eventual consistency is acceptable for ledger systems. While Firestore is highly available, you must treat your ledger operations as strongly consistent. This means you must read the data you need to modify within the transaction block.
Never cache balances in your application layer and write them back without verifying the database state. Even if your latency increases slightly, the correctness of the financial data is the primary KPI. At our scale, we sacrifice microseconds of latency for the guarantee that no double-spend can occur. If your developers suggest moving to a 'faster' model that bypasses transaction checks, they are proposing a system that will inevitably face a reconciliation gap.
Your project structure should reflect this 'read-before-write' requirement. By keeping the logic for transaction boundaries tightly bundled with the document structure, you make it harder for engineers to skip the validation step. As the team grows, documentation becomes secondary; structural constraints become the primary way to enforce quality.
Conclusion: The Maturity Cycle of a Fintech Backend
Growing a Firebase project is about maturity. You start with ease of use, you move to structure, and you end with reliability. For those managing high-throughput pipelines, the structure I have outlined is not just a suggestion—it is a defensive measure. A ledger is only as strong as its weakest transaction. By isolating domains, enforcing atomic writes, and automating the reconciliation process, you can handle ₹2B/day with the confidence that every rupee is accounted for.
Always remember: in the world of fintech, the technology is invisible, but the failures are very public. Keep your writes atomic, your logs immutable, and your reconciliation service running constantly. If you treat your Firestore project as a piece of financial infrastructure rather than a generic document store, your team will find that the system scales not just in throughput, but in the trust it earns from your users and auditors alike. Your project structure is the final line of defense against data integrity issues; ensure it is robust, documented, and enforced with the same rigor you would apply to any traditional ledger system.