← Index
Journal/roombooking·Alixan · 3 min
Alixan Balu

RoomBookingJWT artisanal entre Vue 3 et C# ASP.NET Core 8


POST /booking reçoit un token dans le body de la requête, pas dans le header Authorization. Premier réflexe en lisant le code : c'est une erreur. Deuxième réflexe en regardant le JwtTokenService qui valide la signature HMAC-SHA256 à la main : c'est un choix. Tout le projet tient dans cet écart entre la convention et la décision assumée.

RoomBooking est une application de réservation de salles, développée en M1 à Ynov avec Aurélie Runser. Un catalogue de salles côté Vue 3, une API ASP.NET Core 8 derrière, SQLite pour la persistance. L'intérêt du projet ne tient pas au métier, classique, mais à plusieurs choix techniques qui méritent d'être expliqués.

Trois couches côté front, ASP.NET côté back

roombooking architecture schema

Côté frontend, le projet applique une séparation stricte qu'on voit rarement dans un SPA Vue : domain/models pour les types TypeScript, domain/services pour les appels API métier, infrastructure pour le client Axios et l'auth, application/vue pour les composants et le router. Ce n'est pas une couche d'abstraction gratuite, c'est ce qui permet de remplacer Axios par Fetch ou Vue par Svelte sans toucher au cœur métier.

JWT à la main, sans ASP.NET Identity

Le JwtTokenService signe les tokens en HMAC-SHA256 avec userId et email en claims, et les valide ensuite manuellement à chaque requête : signature, expiration, algorithme. ASP.NET Identity n'est pas utilisé.

Le choix est délibéré. Identity apporte des dizaines de tables, un schéma de migrations imposé, et un poids conceptuel disproportionné pour un projet qui a juste besoin d'authentifier des utilisateurs. Le code en clair, lui, fait à peu près cent lignes et expose ce que JWT fait réellement.

La contrepartie assumée, c'est l'absence de raffinements : pas de refresh token, pas de blacklist, pas de rotation de clé. Et un détail qui dérange volontairement : le token transite parfois dans le body des requêtes sensibles plutôt qu'en header Authorization. La convention REST recommande l'inverse. Le compromis vise la simplicité au prix d'une bonne pratique, et c'est exactement le genre de décision qu'on assume sur un projet étudiant en sachant qu'on la corrigerait en production.

La cohérence temporelle sans scheduler

Une réservation passe automatiquement au statut "Terminée" une fois sa date de fin dépassée. La question : quand effectuer cette mise à jour ?

L'approche orthodoxe demande un job CRON, un scheduler type Hangfire, ou une background task .NET. Le projet fait l'inverse : à chaque appel GET /booking/user/{token}, le backend parcourt les réservations et met à jour les statuts à la volée. Pas de service tournant en permanence, pas d'infra additionnelle.

La limite est claire : tant que personne ne consulte ses réservations, les statuts ne se mettent pas à jour en base. Pour une UI qui se rafraîchit à chaque visite, ça suffit. Pour un système qui déclenche des notifications ou des relances email, ça ne tient plus, et il faudrait basculer sur un vrai scheduler.

Les transactions EF Core pour les créations composées

La création d'une réservation touche trois tables : Bookings, Guests, Equipments. Si l'insertion des invités échoue à mi-parcours, on se retrouve avec une réservation en base sans ses participants. Le projet utilise une transaction explicite via BeginTransaction() qui commit l'ensemble ou rollback en bloc.

C'est l'exemple d'école qui justifie l'existence des transactions. La logique métier impose une atomicité que la base seule ne garantit pas, et EF Core l'expose proprement.

CSV, iCal et Excel depuis les mêmes données

Le BookingExporter produit trois formats à partir d'un même IEnumerable<Booking> :

Format

Lib

Usage

CSV

CsvHelper

Tableurs, analyse

iCal (.ics)

Ical.Net

Google Calendar, Outlook

Excel (.xlsx)

EPPlus

Rapports formatés

Le format iCal mérite un mot. C'est un standard ouvert (RFC 5545) structuré en blocs BEGIN:VCALENDAR / BEGIN:VEVENT, avec des champs ORGANIZER, ATTENDEE, DTSTART. Google Calendar, Outlook, Apple Calendar le lisent tous nativement. Exporter une réservation en .ics revient à donner à l'utilisateur un fichier qu'il glisse-dépose dans son agenda, sans intégration OAuth ni API tierce.

La stack technique

VueTypescript.NET 8ASP.NET CoreEntity Framework CoreSQLiteBCryptDockerNgi


Un détail Docker qui pique souvent : la variable VITE_ROOM_BOOKING_API est injectée au moment du npm run build, pas au runtime. Vite remplace les import.meta.env.VITE_* à la compilation, ce qui veut dire qu'on ne peut pas changer l'URL de l'API en redémarrant le conteneur. Il faut rebuilder. C'est un piège classique pour qui vient de Node ou de Next.js où les process.env sont dynamiques.

Ce que j'en ai retenu

Implémenter JWT à la main une fois dans sa vie change le rapport à la sécurité. On comprend ce que signe une signature, ce que valide une validation, et pourquoi les bibliothèques existent. Après quoi on est mieux équipé pour les utiliser.

Les transactions ne sont pas un raffinement académique. Dès qu'une opération métier touche plusieurs tables, l'atomicité devient un besoin réel, et l'absence de transaction se paye en données corrompues.

Enfin, SQLite tient debout plus longtemps qu'on ne croit. Pour un projet à un seul nœud, sans concurrence d'écriture massive, c'est un choix défendable. La question n'est pas "SQLite ou PostgreSQL", c'est "quel est mon profil de charge". Pour RoomBooking, la réponse n'a jamais cessé d'être SQLite.


Aller plus loin

GitHubExplorer le codegithub.com

Retour à l'index