Skip to content

ADR-0001: Frappe as the Single Source of Truth for Business Data

Status: Accepted Date: 2026-05-03 Deciders: Architecture team Impacted Repos: All

Context

The platform was historically split:

  • bz-api-server (NestJS + DynamoDB) held business data via single-table design.
  • Frontends (bz-customer-web, admin-web-app, etc.) held duplicated state in Redux, local databases, and ad-hoc caches.
  • Migration to Frappe + MariaDB began in 2025.
  • During migration, two parallel "sources of truth" coexisted, causing drift.

Without a clear rule, frontend teams kept adding business entities to local databases (e.g., Drizzle/TiDB tables for "packages", "outlets") because it was faster than waiting for a Frappe doctype. This created multiple drift risks: schema divergence, stale data, and re-implementation of business logic across repos.

Decision

Frappe (reservation-api-server) is the sole source of truth for all business domain data. Every BookingZone repo treats Frappe as canonical. Local stores are restricted to:

  1. Authentication / session state
  2. UI-only operational data (form builder content, AI logs, page builder)
  3. Caching of Frappe data, explicitly labeled as cache
  4. Data without any conceivable Frappe equivalent

If a new business concept emerges, the workflow is:

  1. Create or extend a BZ* doctype in reservation-api-server.
  2. Expose a whitelisted API method.
  3. Frontends consume via the published API contract.

Local Drizzle/TiDB tables for business data are forbidden and any PR introducing them is rejected.

Consequences

Positive

  • Single source of truth eliminates drift.
  • AI agents in any repo can rely on Frappe + the published contract.
  • Simplifies migration completion (no parallel stores to reconcile).
  • Auditability and compliance posture improves materially.

Negative / Trade-offs

  • New features may be slower in the short term — must wait for backend doctype + API method before frontend can build against it.
  • Frontends must handle Frappe API latency and intermittent failures rather than reading from a local DB.

Risks / Mitigations

  • Risk: backend becomes a bottleneck for feature delivery. Mitigation: backend roadmap prioritizes new doctypes; frontends scaffold against generated types in parallel using stubs.
  • Risk: cache layers become a hidden source of truth themselves. Mitigation: caches are explicitly labeled, time-bound, and never authoritative; every cache key resolves to a Frappe primary key.

Alternatives Considered

  • Federated multi-master: each repo owns part of the schema. Rejected: introduces irreconcilable drift, complicates cross-repo reporting.
  • Backend-for-Frontend with local persistence: keep light per-repo stores synced to Frappe. Rejected: doubles the schema work and the drift risk we are trying to eliminate.

Implementation Notes

  • Existing local Drizzle tables for business data in admin-web-app are flagged for migration to Frappe doctypes (separate spec required).
  • Caching pattern reference: server/atp-frappe-packages.ts in admin-web-app.

References

  • system-constitution.md Principle I
  • admin-web-app/FRAPPE_ONLY.md (legacy single-repo enforcement)