ls

ViteComm · Prisma · MySQL

Modeling a Marketplace in Prisma: Roles, Carts, and Reports

One Utilisateur, three hats — how ViteComm models clients, vendors, and drivers with 1:1 relations, dual-sided reports, and push subscriptions.

September 4, 2026 · 7 min read

ViteComm is a multi-vendor marketplace: clients order, vendors sell, drivers deliver, admins oversee. Four roles, one login. The cleanest way I found to model that in Prisma with MySQL is a single user table with one-to-one role extensions.

One table per role, linked 1:1

Every account is a Utilisateur. Each role is its own model sharing the user's primary key:

model Utilisateur {
  id_user       Int     @id @default(autoincrement())
  nom           String
  prenom        String
  email         String  @unique
  statut_compte String  @default("Actif")
  est_admin     Boolean @default(false)

  client   Client?
  vendeur  Vendeur?
  livreur  Livreur?
}

model Client {
  id_user           Int  @id
  utilisateur       Utilisateur @relation(fields: [id_user], references: [id_user], onDelete: Cascade)
  adresse_livraison String
}

A user can wear several hats over time — a client who becomes a vendor keeps one identity, one password, one notification inbox. Roles become data, not accounts.

Reports need both sides

Marketplaces need moderation, and moderation needs context. A report always has an author and a target, so the relation is declared twice:

signalementsAuteur Signalement[] @relation("SignalementsAuteur")
signalementsCible  Signalement[] @relation("SignalementsCible")

Naming both sides explicitly keeps queries readable: reports filed by a user versus reports against them. Prisma requires the disambiguation anyway — lean into it and make the names tell the story.

Push over polling

Order updates, driver assignments, admin notices — a marketplace that polls for these feels dead. The schema carries a PushSubscription model next to Notification, so new events travel outward instead of being asked for. The frontend stays a thin subscriber; the database stays the source of truth.

Names in the domain language

The schema is written in French — Utilisateur, Commande, Panier — because the merchants and drivers who will one day read exports and invoices think in French. An ORM schema is also documentation; write it in the language of the people it serves.

Takeaway

Marketplace schemas fail when roles become separate user tables (duplicate identities) or when moderation is an afterthought (no counters, no context). Model identity once, extend per role, and give every report two sides from day one.