Data model and architecture
This guide explains how Brandow Storage stores and validates data under the hood.
It is useful for technical operators, integration planning, and contributors.
Terminology bridge
- User-facing:
- Code/database:
schema
record
- inline schema +
record_links
Core data invariants
- Row payloads are stored as flat JSON (
records.data).
- Nested structures are represented by linked records, not nested JSON blobs.
- Row validation runs through the schema layer before writes.
- Server/client Supabase usage remains separated by runtime boundary.
These invariants are important for consistency and long-term performance.
Why rows are flat JSON
Flat Row data keeps core operations predictable:
- Validation logic stays simpler and faster
- Queries and indexing strategies are clearer
- Schema changes are easier to manage
- UI forms can map directly to field-value pairs
Modeling relationships correctly
When data needs hierarchy or relational structure:
- Create related Tables/Sub-Tables
- Link Rows through relationship records (
record_links)
- Use reference columns in the parent Table where needed
Do not put nested user objects inside records.data.
Validation flow
Before writing a Row:
- Load the Table definition (columns + options).
- Validate candidate data against dynamic schema rules.
- Return explicit field-level errors if invalid.
- Write only validated data.
This pattern prevents malformed data from bypassing app rules.
Caching and invalidation
Schema definitions are cached for speed. When a Table structure changes:
- Invalidate schema cache for that Table
- Recompute validation shape using latest columns
Without invalidation, stale validation behavior can occur.
Access and tenant boundaries
Brandow Storage uses company-scoped access boundaries:
- Authentication and route checks enforce session + company context
- Role and permission checks gate table-level operations
- Queries should minimize over-fetching and avoid N+1 patterns where possible
AI routes and architecture
AI routes should be designed with bounded context:
- Keep prompt payloads focused
- Limit row sample counts for analysis
- Log enough metadata for debugging without exposing sensitive content
Operational guidance
For production reliability:
- Paginate row-heavy views
- Avoid
select('*') when only a subset is needed
- Batch related queries rather than looping per Row
- Keep expensive AI calls observable and constrained
Related docs