# ADR-004 : Architecture Microservices Hybride **Statut** : ✅ Accepté **Date** : Octobre 2025 **Décideurs** : Équipe Architecture **Tags** : `architecture`, `microservices`, `patterns` --- ## Contexte Le système BricoLoc peut être architecturé de plusieurs façons : - Architecture **monolithique** (comme le legacy) - Architecture **en couches** (layered) - Architecture **microservices** - Architecture **hexagonale** L'objectif est de démontrer la maîtrise de l'architecture logicielle en appliquant des **patterns modernes**. ### Options considérées 1. **Monolithe modulaire** (architecture en couches uniquement) 2. **Microservices purs** (services déployés séparément) 3. **Microservices hybride** (logiquement séparés, physiquement colocalisés) 4. **Architecture hexagonale** (ports & adapters) --- ## Décision ✅ **Nous adoptons une architecture Microservices Hybride avec Clean Architecture interne** **Hybride** signifie : - **Logiquement** : 6 microservices distincts avec responsabilités claires - **Physiquement** : Colocalisés dans le même déploiement Next.js (simplicité) - **Évolutif** : Possibilité de séparer les déploiements si besoin --- ## Justification ### Pourquoi Microservices ? #### 1. **Décomposition par Domaine Métier** 📦 Chaque microservice correspond à un **bounded context** : | Microservice | Responsabilité | Bounded Context | |--------------|----------------|-----------------| | **Auth Service** | Authentification, autorisation | Identity & Access Management | | **Catalogue Service** | Gestion outils, catégories, recherche | Product Catalog | | **Reservation Service** | Réservations, planning | Booking Management | | **Inventory Service** | Stocks, entrepôts, disponibilité | Inventory Management | | **Payment Service** | Paiements, transactions | Payment Processing | | **Notification Service** | Emails, notifications | Communication | **Avantage** : Séparation claire des responsabilités (Single Responsibility Principle) #### 2. **Autonomie** 🚀 Chaque microservice : - A sa propre logique métier (domaine) - Gère ses propres données (database per service - logique) - Peut être développé indépendamment - Peut évoluer sans impacter les autres **Exemple** : Changement de la logique de pricing → uniquement Reservation Service touché #### 3. **Scalabilité Ciblée** 📈 Possibilité de scaler uniquement les services sous charge : - **Inventory Service** (beaucoup d'accès en temps réel) → scale horizontal - Les autres restent à 1 instance **Comparaison Legacy** : Monolithe = scale tout ou rien (coûteux) #### 4. **Isolation des Pannes** 🛡️ Si un service tombe en panne, les autres continuent : - **Payment Service down** → Catalogue et Recherche fonctionnent toujours - **Graceful degradation** possible **Comparaison Legacy** : Monolithe = single point of failure #### 5. **Évolutivité Technologique** 🔧 Chaque microservice pourrait utiliser une techno différente : - Catalogue Service → Node.js (si besoin d'ElasticSearch) - Payment Service → Python (si ML pour fraude detection) - Etc. **Note** : Dans notre cas, tout est Next.js API Routes (simplicité), mais l'architecture permet l'évolution. #### 6. **Démonstration Architecturale** 🏛️ C'est un **projet d'architecture logicielle** : - Microservices = pattern architectural majeur - Démontre la maîtrise de la décomposition - Impressionnant pour un portfolio --- ### Pourquoi "Hybride" ? (Pas microservices purs) #### Microservices Purs (Rejeté) **Avantages** : - Déploiement indépendant par service - Technologies hétérogènes possibles - Scaling précis **Inconvénients** : - ❌ **Complexité opérationnelle** (6 déploiements séparés) - ❌ **Coûts** (6 instances minimum) - ❌ **Latence réseau** (appels HTTP inter-services) - ❌ **Debugging complexe** (logs distribués) - ❌ **Overkill** pour projet académique #### Microservices Hybride (Choisi) **Principe** : - Services logiquement séparés (code organisé en microservices) - Physiquement colocalisés (même déploiement Next.js) - Communication intra-process (pas HTTP externe) **Avantages** : - ✅ **Simplicité opérationnelle** (1 seul déploiement) - ✅ **Performance** (pas de latence réseau) - ✅ **Coûts réduits** (1 seule instance) - ✅ **Debugging facile** (logs centralisés) - ✅ **Démontre l'architecture** sans overhead **Possibilité d'évolution** : - Si besoin de scale → extraire un service en déploiement séparé - Architecture prête pour ça (faible couplage) --- ### Clean Architecture Interne Chaque microservice implémente **Clean Architecture** : ``` microservice/ ├── domain/ # Couche Domaine │ ├── entities/ # Entités métier │ ├── value-objects/ # Value Objects │ ├── repositories/ # Interfaces repositories │ └── rules/ # Règles métier │ ├── application/ # Couche Application │ ├── use-cases/ # Cas d'usage │ ├── services/ # Services applicatifs │ └── dtos/ # Data Transfer Objects │ └── infrastructure/ # Couche Infrastructure ├── repositories/ # Implémentations (Supabase) ├── external-apis/ # APIs externes (Stripe) └── events/ # Event Bus ``` **Bénéfices** : - ✅ Testabilité maximale - ✅ Indépendance des frameworks - ✅ Maintenabilité - ✅ Respect de SOLID --- ## Architecture Proposée ### Vue d'Ensemble ``` ┌───────────────────────────────────────────────────┐ │ Client (Browser) - Next.js SSR │ └─────────────────────┬─────────────────────────────┘ │ ┌─────────────────────▼─────────────────────────────┐ │ API Gateway / BFF (Next.js API) │ │ Orchestration & Aggregation │ └─┬──────┬───────┬───────┬───────┬────────┬────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ┌─────┐┌─────┐┌──────┐┌──────┐┌──────┐┌──────┐ │Auth ││Cata-││Reser-││Inven-││Pay- ││Noti- │ │Svc ││logue││vation││tory ││ment ││fica- │ │ ││Svc ││Svc ││Svc ││Svc ││tion │ │ ││ ││ ││ ││ ││Svc │ └──┬──┘└──┬──┘└───┬──┘└───┬──┘└───┬──┘└───┬──┘ │ │ │ │ │ │ │ │ │ │ │ │ └──────┴───────┴───────┴───────┴────────┘ │ ┌──────▼──────┐ │ Event Bus │ (Supabase Realtime) └──────┬──────┘ │ ┌──────▼──────┐ │ Supabase │ (PostgreSQL + Auth + Storage) └─────────────┘ ``` ### Communication #### Synchrone (REST-like) ```typescript // API Gateway appelle directement le service (même process) const availability = await inventoryService.checkAvailability({ outilId, entrepotId, dateDebut, dateFin }) ``` #### Asynchrone (Events) ```typescript // Service émet un event via Event Bus (Supabase Realtime) await eventBus.publish('reservation.confirmed', { reservationId, outilId, entrepotId }) // Autre service écoute l'event eventBus.subscribe('reservation.confirmed', async (data) => { await inventoryService.reserveStock(data) }) ``` --- ## Conséquences ### ✅ Avantages 1. **Architecture claire** : Décomposition fonctionnelle évidente 2. **Maintenabilité** : Chaque service est focalisé et petit 3. **Testabilité** : Tests unitaires isolés par service 4. **Évolutivité** : Possibilité d'extraire un service si besoin 5. **Démonstration** : Montre la maîtrise de patterns avancés 6. **Simplicité opérationnelle** : 1 seul déploiement 7. **Performance** : Pas de latence réseau ### ⚠️ Complexités 1. **Structure du code** : Plus de dossiers et fichiers 2. **Communication** : Gérer Event Bus pour async 3. **Transactions distribuées** : Saga pattern si nécessaire 4. **Tests d'intégration** : Plus complexes ### 🛠️ Mitigations - **Structure** : Documentation claire (README par service) - **Communication** : Event Bus simple (Supabase Realtime) - **Transactions** : Éviter si possible, ou Saga simplifié - **Tests** : Focus sur tests unitaires par service --- ## Alternatives Rejetées ### ❌ Monolithe Modulaire (Couches uniquement) **Raisons du rejet** : - Moins impressionnant architecturalement - Pas de décomposition fonctionnelle - Moins adapté pour démonstration d'architecture - Scaling "tout ou rien" ### ❌ Microservices Purs (Déploiements séparés) **Raisons du rejet** : - Trop complexe pour projet académique - Coûts hébergement (6 instances) - Overhead opérationnel - Debugging distribué difficile ### ❌ Architecture Hexagonale Seule **Raisons du rejet** : - Pattern complémentaire (on l'utilise en interne) - Moins "visible" que microservices - Moins adapté pour comparaison avec legacy --- ## Migration vers Microservices Purs (Futur) L'architecture permet d'extraire un service facilement : **Étape 1** : Extraire Inventory Service (le plus sollicité) ``` Avant : Next.js (tout colocalisé) Après : Next.js + Inventory Service (Node.js séparé) ``` **Changement minimal** : - Remplacer appel direct par appel HTTP - Déployer Inventory Service séparément - Event Bus déjà en place (pas de changement) **Couplage faible** ✅ → Migration facile --- ## Références - [Microservices Pattern](https://microservices.io/) - [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) - [Domain-Driven Design](https://martinfowler.com/bliki/DomainDrivenDesign.html) - [Bounded Context](https://martinfowler.com/bliki/BoundedContext.html) - [Database per Service](https://microservices.io/patterns/data/database-per-service.html) --- ## Validations - [x] Bounded contexts identifiés - [x] 6 microservices définis - [x] Communication sync/async définie - [x] Clean Architecture par service validée - [x] Event Bus (Supabase Realtime) testé --- **Prochaine révision** : Si complexité trop élevée lors du développement