- README enrichi (présentation Karbé, fonctionnalités, setup dev, env, structure, conventions Git) - ROADMAP.md : phases MVP -> V2 -> V3 - docs/ARCHITECTURE.md : stack, modèle de données (Prisma) et flux principaux Co-Authored-By: Paperclip <noreply@paperclip.ing>
282 lines
12 KiB
Markdown
282 lines
12 KiB
Markdown
# 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).
|
|
```
|