SellVia Docs — menu
DocsDatabaseConstraints

Constraints

Database/Constraints.md
backendUpdated Aug 23, 2026

Constraints

Purpose

What rules the database itself enforces, beyond what the application layer checks — defense in depth for a system handling real money.

Enforced Constraints

  • Non-negative amounts: CHECK (amount_cents >= 0) on offers.price_cents, commissions.amount_cents, platform_fees.amount_cents, payouts.amount_cents — catches bugs before they become financial discrepancies
  • Commission rate bounds: even though there's no platform-enforced business range (01. Business Logic → Business Rules), a sanity-check constraint like CHECK (commission_rate > 0 AND commission_rate <= 1) still belongs at the DB level, to catch obvious data-entry bugs (e.g. someone accidentally storing "20" instead of "0.20") — this is a data-integrity constraint, not a business-rule constraint, and the two shouldn't be confused
  • Currency enum: CHECK (currency IN ('PKR')) on every monetary table — hard-blocks accidentally supporting a currency without a deliberate schema change (updated 2026-08-23, see below)
  • Unique constraints: affiliate_links.slug (globally unique, since it's part of a public URL), applications unique on (offer_id, creator_profile_id) so a creator can't submit duplicate applications to the same offer
  • Status enums: all status fields (offers, applications, sales, payouts) use Postgres enums, not free-text — matches the State Machines doc's defined transitions and makes an invalid status a schema-level impossibility, not just an application bug

Open Questions

  • None blocking — straightforward given the business rules are already settled in 01. Business Logic.

Update (2026-08-23): PKR-Only, Offer Replaces Campaign

Founder decisions, full reasoning in 02. Architecture Decision Log.

  • Currency enum flipped: was CHECK (currency IN ('USD','EUR','GBP')) (with PKR deliberately excluded), now CHECK (currency IN ('PKR')) — the exact reverse. If multi-currency returns post-MVP (Full Product Vision), this constraint is where that change lands.
  • applications' unique constraint moves from (campaign_id, creator_profile_id) to (offer_id, creator_profile_id) — no separate Campaign table (01. Domain Model, 03. Table Specifications).
  • Status enums on the offers table (was campaigns) — same four values (draft/live/paused/ended), same enforcement logic, different table name.