🗄️ Data Architecture
BayanCore's data architecture is designed to handle high-transaction workloads while enforcing strict tenant isolation, local data retention limits, and low-latency database queries.
1. Multi-Tenancy & Tenant Isolation
BayanCore utilizes a shared-database, row-level multi-tenancy model running on isolated container pools:
[Customer A Frontend] ──> [Tenant Gateway A] ──> [Shared MariaDB Instance]
├── Rows filtered by company = "Company_A"
[Customer B Frontend] ──> [Tenant Gateway B] ──> └── Rows filtered by company = "Company_B"
- Logical Isolation: Every document (invoices, ledger entries, purchase orders, employees) is tagged with a mandatory
companyfield. - Query Filtering: All SQL queries originating from user interfaces or background jobs are programmatically appended with a
WHERE company = [tenant_company_id]filter by the Frappe framework layer. - Database Isolation for Enterprises: For enterprise-tier clients, the deployment system provisions dedicated MariaDB instances to guarantee complete physical data isolation.
2. Row-Level Security (RLS) & Permissions
User permission boundaries are enforced at the database row level:
- Document-Level Permissions (Frappe User Roles): Access is controlled using Role-Based Access Control (RBAC). For example:
- Sales Representatives can view and draft only invoices they created (
owner = [user_id]). - Finance Managers can view all invoices but cannot edit them once posted.
- Company Owners have global read-write permissions across all modules.
- Sales Representatives can view and draft only invoices they created (
- No Raw SQL Bypass: The experience layer communicates with the execution layer exclusively through API contracts. Direct raw SQL executions from user clients are programmatically blocked by the API Gateway.
3. Database Engine & Indexing (MariaDB)
MariaDB serves as our primary transactional database engine.
- Partitioning & Indexing:
- Compound indexes are defined on frequently queried tables to prevent full-table scans (e.g.,
invoice(company, posting_date, status)). - Audit log tables are partitioned by year to optimize search performance and simplify retention archives.
- Compound indexes are defined on frequently queried tables to prevent full-table scans (e.g.,
- Dual Calendar Storage: Financial records store Gregorian dates natively (
posting_date). The UI layer converts these dates to Hijri formats programmatically using the client's locale config, ensuring clean reporting alignment with local customs.
4. Redis Cache & Session Store
In-memory data structures are managed using OCI Cache (Redis):
- Session Management: User session tokens (JWTs) are cached in Redis with strict time-to-live (TTL) expiries mapped to Clerk authentication limits.
- Rate-Limit Buckets: IP-based and user-based API requests are throttled using Token Bucket algorithms stored in Redis sorted sets.
- Metadata Caching: Frequently accessed, static compliance schemas (like ZATCA UBL schema templates) are cached in-memory, bypassing database queries during invoice validation.