MVP-Friendly, Future-State-Compatible Backend Architecture Plan
Future-state design document
This document was written as a pre-implementation design spec. The current deployed architecture is described in MVP Architecture. The high-level topology and principles here reflect the intended direction; the domain breakdown (Banking, Transaction, Classification, Reconciliation, Reporting) and advanced features (bank feeds, outbox, Kafka) are not yet implemented.
This document describes a scalable, privacy-first backend architecture designed for:
- A React web client
- Financial data (P&L, reconciliation, auditability)
- PostgreSQL as the source of truth, with Firestore as the real-time read layer
- Bank-feed imports and on-demand polling
- An MVP that can evolve cleanly into microservices (Plan B)
This is intentionally modular, not microservices-first, and avoids early distributed-system complexity.
Architectural Principles
- Modular-first, service-ready boundaries
- Single transactional system of record
- Append-only financial data
- Strong tenant isolation
- Explicit auditability
- Async-ready without mandatory eventing
- Clear ownership of data per domain
High-Level Topology
- React web client
- Node.js/Fastify REST API (BFF / API Edge)
- PostgreSQL on Google Cloud SQL (schema-per-domain, source of truth)
- Firestore (real-time read layer, synced from PostgreSQL via pg_notify)
- Firestore sync service (long-running
pg_notifylistener, deployed as a separate Cloud Run service)
Layered Backend Architecture (Logical Layers)
1. API / Edge Layer (BFF)
- Authentication via Firebase JWT (verified with Firebase Admin SDK)
- Tenant resolution
- Authorization (RBAC)
- Request validation
- Rate limiting
- Response shaping for web clients
- REST (Fastify)
- Correlation ID injection
2. Application / Orchestration Layer
- Use-case coordination
- Job orchestration (imports, polling)
- Transaction boundary management
- Calls into domain services
- No business rules
- No persistence logic
3. Domain Layer (Service-Ready Modules)
Each module owns:
- Its business rules
- Its write model
- Its database schema
Banking / Ingestion Domain
- Bank connection management
- Provider adapters
- One-time imports
- On-demand polling
- Raw transaction ingestion
- Import job lifecycle
Transaction Domain
- Normalized transactions
- Deduplication logic
- Pending vs posted handling
- Source attribution
Classification Domain
- Chart of Accounts (flat for MVP)
- Classification rules
- Manual overrides
- Uncategorized handling
Ledger Domain
- Immutable ledger entries
- Double-entry enforcement
- Posting rules
- Period awareness
Reconciliation Domain
- Account balances
- Cleared vs uncleared
- Reconciliation periods
- Period close and lock
Reporting Domain
- P&L generation
- Cross-account aggregation
- Statement generation
- Drill-down read models
Audit Domain
- Append-only audit log
- Change tracking
- Access history
- Compliance evidence
4. Persistence Layer
- PostgreSQL
- Schema-per-domain (e.g. ledger., banking.)
- Single writer per table
- No cross-domain foreign keys
- Views or read models for cross-domain reads
- Flyway migrations per schema
5. Async & Integration Layer (MVP-safe)
- Deployed:
pg_notifytriggers → Node.js sync listener → Firestore (simple, low-latency, no extra infrastructure) - Future: Job tables (import_job), outbox table (event-shaped records)
- Future: Kafka-compatible event log (or Datastream if staying on Google Cloud SQL)
6. Security & Privacy Layer
- Tenant isolation enforced at service layer
- Field-level encryption for sensitive data
- No PII in logs
- Immutable audit trails
- Admin override with reason codes
7. Observability & Ops Layer
- Structured logging
- Correlation IDs
- Metrics per domain
- Health checks
- Data completeness checks
MVP Scope Summary
Deployed (current MVP):
- Entities, accounts, journal entries, parties (ledger schema)
- IAM — organizations, memberships, roles
- Firestore sync via
pg_notify - Audit trail
Planned (not yet implemented):
- Bank ingestion and transaction normalization
- Reconciliation
- P&L and statements
Explicitly excluded (Phase 2+):
- Accruals
- Budgeting
- Tax automation
- Multi-entity consolidation
- Event-driven microservices
Evolution Path (Plan B)
- Extract domains into services as needed
- Promote outbox to Kafka
- Split Postgres by schema ownership
- Add read replicas and materialized views
- Preserve API contracts at BFF
Non-Negotiables
- Ledger is append-only
- Corrections are new entries
- Periods are lockable
- Every report number is traceable
- PostgreSQL is the system of record — Firestore is a read layer, not a data store