Table des matières
Phase 2 — Prisma Schema
Objectif
Construire le schéma Prisma qui deviendra la source de vérité unique du système.
À partir de ce schéma seront générés :
- PostgreSQL
- Prisma Client
- DTO
- Entités NestJS
- OpenAPI
- SDK TypeScript
- Migrations SQL
Le schéma doit refléter l'intégralité des domaines définis dans les 20 sprints.
Objectifs techniques
Le schéma Prisma doit supporter :
Multi-tenant Audit Historisation Soft Delete RBAC CRM Catalogue Réservations Paiements Contrats OTA IA Revenue Management Internationalisation Sécurité Enterprise
Ordre de construction recommandé
Ne jamais commencer par les 80 tables.
Construire par couches.
Couche 1 — Fondations
Construire en premier :
Tenant User Role Permission UserRole RolePermission
Ces tables sont nécessaires partout.
Couche 2 — Référentiels
Créer :
Address Country Language Currency Timezone
Couche 3 — Catalogue
Créer :
Property PropertyType PropertyMedia PropertyFeature PropertyAvailability PropertyRate
Couche 4 — Réservations
Créer :
Reservation ReservationGuest ReservationStatus ReservationEvent
Couche 5 — Contrats
Créer :
Contract ContractTemplate ContractSignature Document
Couche 6 — Paiements
Créer :
Payment Invoice Refund AccountingEntry
Couche 7 — CRM
Créer :
Lead Customer Activity Task Pipeline
Couche 8 — Propriétaires
Créer :
Owner OwnerProperty OwnerDocument
Couche 9 — Notifications
Créer :
Notification NotificationTemplate Message Campaign
Couche 10 — Administration
Créer :
AuditLog FeatureFlag CustomField Workflow
Couche 11 — IA
Créer :
AiConversation AiMessage KnowledgeDocument AutomationRule
Couche 12 — OTA
Créer :
Channel ChannelConnection PropertyDistribution ChannelReservation
Couche 13 — Revenue Management
Créer :
PricingRule DynamicPrice RevenueSimulation CompetitorSnapshot
Couche 14 — Sécurité
Créer :
Consent SecurityPolicy Risk ComplianceAudit
Couche 15 — Internationalisation
Créer :
Translation CurrencyRate Region EnterpriseLicense
Organisation du dossier Prisma
Au lieu d'un unique fichier géant :
prisma/ ├── schema.prisma │ ├── models/ │ │ ├── tenant.prisma │ ├── user.prisma │ ├── property.prisma │ ├── reservation.prisma │ ├── contract.prisma │ ├── payment.prisma │ ├── crm.prisma │ ├── owner.prisma │ ├── notification.prisma │ ├── audit.prisma │ ├── ai.prisma │ ├── channel.prisma │ ├── revenue.prisma │ ├── security.prisma │ └── localization.prisma
Configuration initiale
schema.prisma
Version minimale :
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
Première implémentation
Étape 1
Créer :
prisma/models/tenant.prisma
Tenant
model Tenant {
id String @id @default(uuid())
code String @unique
name String
status TenantStatus
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users User[]
properties Property[]
}
Enum
enum TenantStatus {
ACTIVE
SUSPENDED
TRIAL
ARCHIVED
}
Étape 2
Créer :
prisma/models/user.prisma
User
model User {
id String @id @default(uuid())
tenantId String
email String @unique
passwordHash String
firstName String
lastName String
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
roles UserRole[]
}
Role
model Role {
id String @id @default(uuid())
code String @unique
name String
userRoles UserRole[]
permissions RolePermission[]
}
Permission
model Permission {
id String @id @default(uuid())
code String @unique
name String
rolePermissions RolePermission[]
}
UserRole
model UserRole {
userId String
roleId String
assignedAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
role Role @relation(
fields:[roleId],
references:[id]
)
@@id([userId, roleId])
}
RolePermission
model RolePermission {
roleId String
permissionId String
role Role @relation(
fields:[roleId],
references:[id]
)
permission Permission @relation(
fields:[permissionId],
references:[id]
)
@@id([roleId, permissionId])
}
Génération
Après chaque bloc important :
npx prisma format
Validation
npx prisma validate
Génération Client
npx prisma generate
Migration
npx prisma migrate dev \
--name init_security
Vérification
Ouvrir :
npx prisma studio
Vérifier la présence :
Tenant User Role Permission UserRole RolePermission
Phase 2-A — Fondation complète (Tenant + RBAC + Propriétés de sécurité)
Vous avez raison.
La version précédente contient :
- Tenant
- User
- Role
- Permission
- UserRole
- RolePermission
mais il manque plusieurs éléments indispensables pour un véritable système Enterprise :
- propriétés du compte utilisateur
- préférences utilisateur
- sessions
- refresh tokens
- MFA
- audit de connexion
- catalogue complet des permissions
Sans ces éléments, le Sprint 1 (Authentification) et le Sprint 19 (Sécurité Enterprise) ne pourront pas être implémentés proprement.
Modèle User complet
User
model User {
id String @id @default(uuid())
tenantId String
email String @unique
passwordHash String
firstName String
lastName String
phone String?
avatarUrl String?
languageCode String?
timezoneCode String?
active Boolean @default(true)
emailVerified Boolean @default(false)
lastLoginAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
roles UserRole[]
sessions UserSession[]
refreshTokens RefreshToken[]
preferences UserPreference?
loginHistory LoginHistory[]
mfaMethods UserMfaMethod[]
}
Préférences utilisateur
UserPreference
model UserPreference {
id String @id @default(uuid())
userId String @unique
theme String?
language String?
timezone String?
notifications Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(
fields:[userId],
references:[id]
)
}
Gestion des sessions
UserSession
model UserSession {
id String @id @default(uuid())
userId String
ipAddress String?
userAgent String?
country String?
city String?
lastActivityAt DateTime
expiresAt DateTime
revokedAt DateTime?
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
Refresh Tokens
RefreshToken
model RefreshToken {
id String @id @default(uuid())
userId String
tokenHash String
expiresAt DateTime
revokedAt DateTime?
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
Historique des connexions
LoginHistory
model LoginHistory {
id String @id @default(uuid())
userId String
success Boolean
ipAddress String?
userAgent String?
country String?
city String?
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
MFA
UserMfaMethod
model UserMfaMethod {
id String @id @default(uuid())
userId String
methodType String
secret String?
enabled Boolean @default(true)
createdAt DateTime @default(now())
user User @relation(
fields:[userId],
references:[id]
)
}
RBAC Enterprise
Role
model Role {
id String @id @default(uuid())
code String @unique
name String
description String?
systemRole Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userRoles UserRole[]
permissions RolePermission[]
}
Permission
model Permission {
id String @id @default(uuid())
code String @unique
name String
description String?
module String
createdAt DateTime @default(now())
rolePermissions RolePermission[]
}
Catalogue initial des permissions
Auth
AUTH_LOGIN AUTH_REGISTER AUTH_RESET_PASSWORD AUTH_MANAGE_USERS
Users
USER_READ USER_CREATE USER_UPDATE USER_DELETE
Properties
PROPERTY_READ PROPERTY_CREATE PROPERTY_UPDATE PROPERTY_DELETE PROPERTY_PUBLISH
Reservations
RESERVATION_READ RESERVATION_CREATE RESERVATION_UPDATE RESERVATION_CANCEL
Contracts
CONTRACT_READ CONTRACT_CREATE CONTRACT_SIGN
Payments
PAYMENT_READ PAYMENT_CREATE PAYMENT_REFUND
CRM
CRM_READ CRM_WRITE CRM_EXPORT
Administration
ADMIN_READ ADMIN_WRITE ADMIN_AUDIT ADMIN_TENANT
Rôles système
SUPER_ADMIN
Accès complet plateforme.
TENANT_ADMIN
Administration d'agence.
AGENCY_MANAGER
Gestion opérationnelle.
OWNER
Extranet propriétaire.
AGENT
Collaborateur.
CUSTOMER
Client final.
Indexes recommandés
User
@@index([tenantId]) @@index([email]) @@index([active])
LoginHistory
@@index([userId]) @@index([createdAt])
UserSession
@@index([userId]) @@index([expiresAt])
Architecture du domaine Property
Entités
Property PropertyType PropertyStatus PropertyAddress PropertyFeature PropertyMedia PropertyAvailability PropertyRate PropertyOwner
Property
Table principale
model Property {
id String @id @default(uuid())
tenantId String
propertyTypeId String
code String @unique
reference String?
title String
slug String @unique
description String?
shortDescription String?
maxGuests Int
bedrooms Int
bathrooms Int
area Decimal? @db.Decimal(10,2)
floor Int?
constructionYear Int?
checkInTime String?
checkOutTime String?
active Boolean @default(true)
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
propertyType PropertyType @relation(
fields:[propertyTypeId],
references:[id]
)
address PropertyAddress?
features PropertyFeature[]
media PropertyMedia[]
availabilities PropertyAvailability[]
rates PropertyRate[]
owners PropertyOwner[]
reservations Reservation[]
}
PropertyType
Référentiel
model PropertyType {
id String @id @default(uuid())
code String @unique
name String
properties Property[]
}
Valeurs
HOUSE APARTMENT VILLA STUDIO LOFT CHALET COTTAGE MOBILE_HOME
PropertyAddress
Adresse du bien
model PropertyAddress {
id String @id @default(uuid())
propertyId String @unique
addressLine1 String
addressLine2 String?
postalCode String
city String
state String?
countryCode String
latitude Decimal? @db.Decimal(10,7)
longitude Decimal? @db.Decimal(10,7)
property Property @relation(
fields:[propertyId],
references:[id]
)
}
PropertyFeature
Équipements
model PropertyFeature {
id String @id @default(uuid())
propertyId String
featureCode String
featureValue String?
property Property @relation(
fields:[propertyId],
references:[id]
)
}
Exemples
POOL WIFI PARKING AIR_CONDITIONING TERRACE SEA_VIEW PET_ALLOWED
PropertyMedia
Médias
model PropertyMedia {
id String @id @default(uuid())
propertyId String
fileName String
fileUrl String
mediaType String
position Int
isCover Boolean @default(false)
createdAt DateTime @default(now())
property Property @relation(
fields:[propertyId],
references:[id]
)
}
MediaType
IMAGE VIDEO VIRTUAL_TOUR DOCUMENT
PropertyAvailability
Calendrier
model PropertyAvailability {
id String @id @default(uuid())
propertyId String
startDate DateTime
endDate DateTime
status AvailabilityStatus
property Property @relation(
fields:[propertyId],
references:[id]
)
}
Enum
enum AvailabilityStatus {
AVAILABLE
RESERVED
BLOCKED
MAINTENANCE
}
PropertyRate
Tarification
model PropertyRate {
id String @id @default(uuid())
propertyId String
startDate DateTime
endDate DateTime
nightlyRate Decimal @db.Decimal(10,2)
weekendRate Decimal? @db.Decimal(10,2)
cleaningFee Decimal? @db.Decimal(10,2)
securityDeposit Decimal? @db.Decimal(10,2)
currencyCode String
property Property @relation(
fields:[propertyId],
references:[id]
)
}
PropertyOwner
Association propriétaire
model PropertyOwner {
propertyId String
ownerId String
ownershipRate Decimal @db.Decimal(5,2)
property Property @relation(
fields:[propertyId],
references:[id]
)
owner Owner @relation(
fields:[ownerId],
references:[id]
)
@@id([propertyId, ownerId])
}
Indexes
Property
@@index([tenantId]) @@index([propertyTypeId]) @@index([published]) @@index([active]) @@index([title]) @@index([slug])
PropertyAvailability
@@index([propertyId]) @@index([startDate]) @@index([endDate])
PropertyRate
@@index([propertyId]) @@index([startDate]) @@index([endDate])
Domaine Owner
Architecture
Owner OwnerAddress OwnerDocument OwnerBankAccount PropertyOwner
Owner
model Owner {
id String @id @default(uuid())
tenantId String
code String @unique
companyName String?
firstName String?
lastName String?
email String
phone String?
mobile String?
taxIdentifier String?
vatNumber String?
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
address OwnerAddress?
documents OwnerDocument[]
bankAccounts OwnerBankAccount[]
properties PropertyOwner[]
}
OwnerAddress
model OwnerAddress {
id String @id @default(uuid())
ownerId String @unique
addressLine1 String
addressLine2 String?
postalCode String
city String
state String?
countryCode String
owner Owner @relation(
fields:[ownerId],
references:[id]
)
}
OwnerDocument
model OwnerDocument {
id String @id @default(uuid())
ownerId String
documentType String
fileUrl String
createdAt DateTime @default(now())
owner Owner @relation(
fields:[ownerId],
references:[id]
)
}
Document Types
IDENTITY TAX_DOCUMENT MANDATE BANK_DETAILS INSURANCE
OwnerBankAccount
model OwnerBankAccount {
id String @id @default(uuid())
ownerId String
iban String
bic String?
accountHolder String
active Boolean @default(true)
owner Owner @relation(
fields:[ownerId],
references:[id]
)
}
Domaine Reservation
Architecture
Reservation ReservationGuest ReservationStatusHistory ReservationEvent ReservationPricing
Reservation
Table centrale
model Reservation {
id String @id @default(uuid())
tenantId String
propertyId String
customerId String?
reference String @unique
status ReservationStatus
checkInDate DateTime
checkOutDate DateTime
nights Int
adults Int
children Int
infants Int
totalGuests Int
notes String?
source ReservationSource
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
cancelledAt DateTime?
tenant Tenant @relation(
fields:[tenantId],
references:[id]
)
property Property @relation(
fields:[propertyId],
references:[id]
)
guests ReservationGuest[]
events ReservationEvent[]
statusHistory ReservationStatusHistory[]
pricing ReservationPricing?
}
ReservationStatus
enum ReservationStatus {
DRAFT
PENDING
CONFIRMED
SIGNED
PAID
CHECKED_IN
COMPLETED
CANCELLED
}
ReservationSource
enum ReservationSource {
WEBSITE
BACKOFFICE
AIRBNB
BOOKING
VRBO
API
}
ReservationGuest
model ReservationGuest {
id String @id @default(uuid())
reservationId String
firstName String
lastName String
birthDate DateTime?
email String?
phone String?
isPrimary Boolean @default(false)
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
ReservationPricing
model ReservationPricing {
id String @id @default(uuid())
reservationId String @unique
nightlyAmount Decimal @db.Decimal(10,2)
cleaningFee Decimal @db.Decimal(10,2)
touristTax Decimal @db.Decimal(10,2)
discountAmount Decimal @db.Decimal(10,2)
totalAmount Decimal @db.Decimal(10,2)
currencyCode String
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
ReservationEvent
model ReservationEvent {
id String @id @default(uuid())
reservationId String
eventType String
payload Json?
createdAt DateTime @default(now())
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
Event Types
CREATED CONFIRMED SIGNED PAID CHECK_IN CHECK_OUT CANCELLED
ReservationStatusHistory
model ReservationStatusHistory {
id String @id @default(uuid())
reservationId String
previousStatus ReservationStatus?
newStatus ReservationStatus
changedAt DateTime @default(now())
reservation Reservation @relation(
fields:[reservationId],
references:[id]
)
}
Relations à ajouter
Property
Ajouter :
reservations Reservation[]
Tenant
Ajouter :
owners Owner[] reservations Reservation[]
Indexes
Owner
@@index([tenantId]) @@index([email]) @@index([active])
Reservation
@@index([tenantId]) @@index([propertyId]) @@index([status]) @@index([checkInDate]) @@index([checkOutDate]) @@index([reference])
ReservationGuest
@@index([reservationId])
Validation
npx prisma format npx prisma validate npx prisma generate
Migration
npx prisma migrate dev \
--name owners_and_reservations
État actuel du schéma
À ce stade, le noyau fonctionnel est enfin présent :
Tenant User Role Permission Owner Property Reservation
avec :
Catalogue Immobilier Propriétaires Réservations RBAC Multi-tenant
Étape suivante
Phase 2-E
Domaines financiers et contractuels :
Contract ContractTemplate ContractSignature Document Payment Invoice Refund AccountingEntry
Ces tables permettront d'implémenter intégralement :
- Sprint 5 — Contrats & Signature Électronique
- Sprint 6 — Paiements & Facturation
et de disposer du premier flux métier complet :
Property ↓ Reservation ↓ Contract ↓ Payment ↓ Invoice