Skip to main content

Database Normalization and Design Review

Summary​

The current schema is normalized for the core transactional model while intentionally using JSON fields for flexible external data and metadata.

AreaStatusNotes
1NFPassCore attributes are scalar; JSON is used for flexible metadata and external payloads
2NFPassAll models use single-column UUID primary keys
3NFPassWABA, phone number, wallet, transaction, and campaign data are separated
BCNFPassCandidate keys and unique constraints are explicit
Multi-tenancyPassTenant-owned data is scoped by vendorId
Shared WABA designPassShared WABAs are platform resources; message tenant isolation is in MessageLog.vendorId
Dedicated WABA designPassVendor.dedicatedWabaId references WabaAccount; exclusivity is application-enforced
Message auditabilityPassrecipientMobile, phoneNumberId, wabaId, payload, pricing, and lifecycle timestamps are retained

JSON Field Justification​

FieldPurpose
Vendor.settingsFlexible tenant configuration
Contact.attributesTenant-defined contact metadata
MessageLog.payloadMeta/API debug payload
MessageLog.pricingBilling data from provider
Template.variablesTemplate placeholder metadata

These fields do not replace core relational structure.

Tenant Isolation​

Tenant-owned models:

  • User
  • Contact
  • Campaign
  • MessageLog
  • Wallet
  • Template
  • Webhook
  • ApiLog

Indirectly scoped models:

  • Transaction through Wallet
  • PhoneNumber through WabaAccount usage and MessageLog routing
  • WabaAccount as a platform resource

Idempotency​

Message idempotency is scoped per tenant:

@@unique([vendorId, idempotencyKey])

This allows different vendors to reuse the same external idempotency key without conflicts.

WABA Normalization​

WabaAccount is a single unified model for shared and dedicated WABAs:

isShared Boolean @default(true)

Shared WABAs are not directly tied to vendors. Dedicated assignment is represented by:

Vendor.dedicatedWabaId

The application layer enforces dedicated assignment rules.

MessageLog Design​

MessageLog intentionally duplicates some trace data:

FieldReason
phoneNumberIdRequired routing and sender audit
wabaIdAnalytics without joining through PhoneNumber
recipientMobileImmutable recipient identity
payloadDebugging provider/API data
pricingBilling reconciliation

This is controlled denormalization for high-volume operational queries.