# Architecture — Karbé Ce document décrit l'architecture technique de **Karbé**, la marketplace de location de carbets fluviaux de Guyane : la **stack**, le **modèle de données** et les **flux principaux**. - Pour démarrer le projet, voir le [README](../README.md). - Pour la trajectoire produit, voir la [roadmap](../ROADMAP.md). ## Sommaire - [Vue d'ensemble](#vue-densemble) - [Stack technique](#stack-technique) - [Organisation du code](#organisation-du-code) - [Modèle de données](#modèle-de-données) - [Flux principaux](#flux-principaux) - [Sécurité & conformité](#sécurité--conformité) ## Vue d'ensemble Karbé est une application **Next.js (App Router)** full-stack : le même projet sert le rendu des pages (React Server Components + Client Components) et expose la logique serveur (route handlers, server actions). La persistance est assurée par **PostgreSQL** via **Prisma**. L'authentification repose sur **NextAuth**, et les paiements sur **Stripe**. ```text ┌─────────────────────────────────────────────┐ Navigateur │ Next.js (App Router) │ (voyageur / │ │ hôte / admin)│ React Server / Client Components │ │ │ Route handlers / Server actions │ │ HTTPS │ │ │ │ └───────▶│ NextAuth Prisma Client │ │ (sessions, (requêtes) │ │ rôles) │ │ └─────────────────────────────────┼─────────────┘ │ ┌────────────────────────┼───────────────┐ ▼ ▼ ▼ PostgreSQL Stripe Stockage (données) (paiements) médias (photos) ``` > **Note de version :** ce dépôt utilise une version récente de Next.js dont > certaines API peuvent différer de la documentation publique. La référence > embarquée se trouve dans `node_modules/next/dist/docs/`. ## Stack technique | Couche | Technologie | Rôle | | --- | --- | --- | | Framework | **Next.js 16** (App Router) | Rendu SSR/RSC, routing, logique serveur. | | UI | **React 19** + **Tailwind CSS v4** | Composants et styles. | | Langage | **TypeScript** | Typage statique de bout en bout. | | ORM | **Prisma 7** | Accès aux données, migrations, typage. Client généré dans `src/generated/prisma`. | | Base de données | **PostgreSQL** | Persistance relationnelle. | | Authentification | **NextAuth** | Sessions et contrôle d'accès par rôle. | | Paiement | **Stripe** | Encaissement réservations + abonnement loueur. | | Lint | **ESLint** (`eslint-config-next`) | Qualité de code. | ## Organisation du code ```text src/ ├── app/ # App Router : pages, layouts, route handlers │ ├── layout.tsx # Layout racine │ ├── page.tsx # Page d'accueil │ └── globals.css # Styles globaux (Tailwind) └── generated/prisma/ # Client Prisma généré (NE PAS éditer à la main) prisma/ ├── schema.prisma # Source de vérité du modèle de données └── migrations/ # Migrations SQL ``` Principes : - **App Router** — chaque dossier de `src/app` est un segment de route. Les pages sont des **Server Components** par défaut ; on bascule en Client Component (`"use client"`) seulement pour l'interactivité. - **Accès données côté serveur** — les requêtes Prisma s'exécutent côté serveur (Server Components, route handlers, server actions), jamais dans le navigateur. - **Client Prisma généré** — importé depuis `src/generated/prisma` ; il est régénéré via `npx prisma generate` (script `postinstall`). ## Modèle de données Le modèle est défini dans [`prisma/schema.prisma`](../prisma/schema.prisma) et porté par PostgreSQL. Il couvre cinq grands domaines : **comptes**, **offre (carbets)**, **réservation**, **paiement** et **relation (messagerie & avis)**. ### Énumérations | Enum | Valeurs | | --- | --- | | `UserRole` | `TRAVELER`, `HOST`, `ADMIN` | | `CarbetStatus` | `DRAFT`, `PUBLISHED`, `ARCHIVED` | | `BookingStatus` | `PENDING`, `CONFIRMED`, `CANCELLED`, `COMPLETED` | | `PaymentStatus` | `PENDING`, `AUTHORIZED`, `SUCCEEDED`, `FAILED`, `REFUNDED` | | `MessageSenderType` | `TRAVELER`, `HOST`, `SYSTEM` | ### Schéma relationnel ```mermaid erDiagram User ||--o| HostProfile : "a (si hôte)" User ||--o{ Booking : "réserve" User ||--o{ Review : "rédige" User ||--o{ FavoriteCarbet : "favoris" HostProfile ||--o{ Carbet : "publie" Carbet ||--o{ CarbetPhoto : "photos" Carbet ||--o{ CarbetAvailability : "disponibilités" Carbet ||--o{ Booking : "réservations" Carbet ||--o{ Review : "avis" Carbet ||--o{ FavoriteCarbet : "favoris" Carbet }o--o{ Amenity : "équipements (CarbetAmenity)" Booking ||--o{ Payment : "paiements" Booking ||--o| Conversation : "fil de discussion" Booking ||--o| Review : "avis" Conversation ||--o{ Message : "messages" ``` ### Entités principales #### Comptes - **`User`** — compte unique pour tous les rôles (`role` : `TRAVELER`, `HOST`, `ADMIN`). Champs clés : `email` (unique), `passwordHash`, `firstName`, `lastName`, `phone?`, `avatarUrl?`, `isActive`. Relations : un éventuel `HostProfile`, ses `bookings`, ses `reviews`, ses `favoriteCarbets`, et ses conversations (en tant que voyageur ou hôte). - **`HostProfile`** — profil hôte en **1:1** avec `User` (relation optionnelle : seuls les hôtes en ont un). Contient `bio?`, `verificationAt?` (date de vérification), `payoutInfo?` (infos de versement) et la liste des `carbets`. #### Offre (carbets) - **`Carbet`** — l'annonce. Champs : `title`, `slug` (unique, pour le SEO), `description`, localisation (`river`, `locality`, `latitude?`, `longitude?`), capacité (`maxGuests`, `bedrooms`, `beds`, `bathrooms`), tarification (`basePricePerNight`, `cleaningFee`, `serviceFee`), `status` (`DRAFT`/`PUBLISHED`/`ARCHIVED`) et `publishedAt?`. Indexé par hôte, statut, (fleuve, localité) et prix pour la recherche. - **`Amenity`** / **`CarbetAmenity`** — catalogue d'équipements et table de liaison **N:N** entre carbets et équipements (clé composite `[carbetId, amenityId]`). - **`CarbetPhoto`** — photos d'un carbet (`url`, `alt?`, `sortOrder`). - **`CarbetAvailability`** — calendrier : une ligne par `date` et par carbet (`@@unique([carbetId, date])`), avec `isAvailable`, `customPrice?` (tarif spécifique) et `minNights` (séjour minimum). #### Réservation - **`Booking`** — réservation reliant un `Carbet` et un `User` (voyageur). Champs : `checkIn`, `checkOut`, `guests`, `status`, instantané de tarification (`nightlyRate`, `cleaningFee`, `serviceFee`, `totalAmount`, `currency` = `EUR`), `notes?`, et champs d'annulation (`canceledAt?`, `cancellationReason?`). Les suppressions de carbet/voyageur sont en `Restrict` pour préserver l'historique des réservations. #### Paiement - **`Payment`** — un ou plusieurs paiements rattachés à une `Booking`. Champs : `provider`, `providerPaymentId?` (unique, ex. id Stripe), `amount`, `currency`, `status` (`PENDING` → `AUTHORIZED` → `SUCCEEDED`/`FAILED`, `REFUNDED`), `paidAt?`, `refundedAt?` et détails d'échec (`failureCode?`, `failureMessage?`). #### Relation : messagerie & avis - **`Conversation`** — un fil **1:1 avec une `Booking`**, reliant le voyageur (`travelerId`) et l'hôte (`hostId`). - **`Message`** — message d'une conversation (`senderType` : `TRAVELER`, `HOST` ou `SYSTEM`, `senderUserId?`, `content`, `sentAt`, `readAt?`). - **`Review`** — avis **1:1 avec une `Booking`** (un avis par séjour), portant une `rating`, un `title?` et un `comment?`, rattaché au carbet et au voyageur. - **`FavoriteCarbet`** — table de liaison **N:N** (wishlist) entre `User` et `Carbet` (clé composite `[userId, carbetId]`). ### Conventions - **Identifiants** : `cuid()` (chaînes) en clé primaire. - **Horodatage** : `createdAt` / `updatedAt` sur la plupart des entités. - **Montants** : `Decimal(10,2)` pour les prix, `Decimal(9,6)` pour les coordonnées GPS. - **Suppressions** : `Cascade` pour les données dépendantes (photos, messages, disponibilités) ; `Restrict` pour préserver l'historique financier (réservations, paiements, avis). - **Index** : posés sur les colonnes de recherche/jointure fréquentes (statut, fleuve+localité, prix, dates, clés étrangères). ## Flux principaux ### 1. Authentification & rôles ```mermaid sequenceDiagram actor U as Utilisateur participant App as Next.js participant Auth as NextAuth participant DB as PostgreSQL U->>App: Inscription / Connexion (email + mot de passe) App->>Auth: Vérifie les identifiants Auth->>DB: Lit User (passwordHash, role, isActive) Auth-->>App: Session (id + role) App-->>U: Accès adapté au rôle (TRAVELER / HOST / ADMIN) ``` Le `role` porté par la session conditionne l'accès : espace voyageur, interface hôte (gestion des carbets), ou back-office admin. ### 2. Publication d'un carbet (hôte) 1. Un `User` de rôle `HOST` (avec `HostProfile`) crée un `Carbet` en `DRAFT`. 2. Il ajoute des `CarbetPhoto`, sélectionne des `Amenity` (via `CarbetAmenity`) et renseigne le calendrier `CarbetAvailability` (dates, prix, nuits min). 3. Il publie : `status` passe à `PUBLISHED` et `publishedAt` est renseigné. Le carbet devient visible dans la recherche publique. ### 3. Recherche & consultation (public, SSR/SEO) 1. La page de listing interroge les `Carbet` en `PUBLISHED` (filtres : fleuve, localité, capacité, prix…), rendue **côté serveur** pour l'indexation. 2. La fiche carbet est servie via son `slug` unique (URL stable, SEO-friendly) et affiche photos, équipements, disponibilités et avis. ### 4. Réservation & paiement ```mermaid sequenceDiagram actor V as Voyageur participant App as Next.js participant DB as PostgreSQL participant Stripe as Stripe V->>App: Choisit des dates + nombre de voyageurs App->>DB: Vérifie CarbetAvailability (dispo + minNights) App->>App: Calcule le total (nuitées, ménage, frais de service) App->>DB: Crée Booking (status = PENDING) V->>App: Procède au paiement App->>Stripe: Crée l'intention de paiement Stripe-->>App: Webhook / callback (succès ou échec) alt Paiement réussi App->>DB: Payment = SUCCEEDED, Booking = CONFIRMED App-->>V: Confirmation de réservation else Paiement échoué App->>DB: Payment = FAILED, Booking reste PENDING App-->>V: Échec, nouvelle tentative possible end ``` À l'issue du séjour, la `Booking` passe en `COMPLETED`. Une annulation renseigne `canceledAt`/`cancellationReason` et peut déclencher un `REFUNDED` côté `Payment`. ### 5. Messagerie À la création d'une réservation, une `Conversation` (1:1 avec la `Booking`) est ouverte entre le voyageur et l'hôte. Les `Message` portent un `senderType` (`TRAVELER`, `HOST`, `SYSTEM` pour les notifications automatiques) et un statut de lecture (`readAt`). ### 6. Avis Après un séjour `COMPLETED`, le voyageur peut déposer un `Review` (un seul par réservation, contrainte d'unicité sur `bookingId`). La note alimente la réputation du carbet affichée sur sa fiche. ## Sécurité & conformité - **Mots de passe** stockés hachés (`passwordHash`), jamais en clair. - **Secrets** (`AUTH_SECRET`/`NEXTAUTH_SECRET`, clés Stripe, `DATABASE_URL`) en variables d'environnement, hors du dépôt (voir `.env.example`). - **Contrôle d'accès** par rôle (`UserRole`) appliqué côté serveur. - **Intégrité financière** : suppressions en `Restrict` sur les réservations, paiements et avis pour conserver l'historique. - **RGPD & mentions légales** : pages dédiées (CGV, politique de confidentialité, mentions légales) — voir la [roadmap](../ROADMAP.md). ```