============================ Architecture Globale ============================ Vue d'ensemble ============== Updo Backend suit les principes du **Domain-Driven Design (DDD)** avec une architecture en **Bounded Contexts** indépendants. Chaque contexte encapsule ses propres modèles, services, événements et vues API. Structure des répertoires ========================= .. code-block:: text updo_backend/ ├── updo/ # Configuration Django │ ├── settings.py # Paramètres du projet │ ├── urls.py # Routes URL racine │ ├── celery.py # Configuration Celery │ └── exception_handler.py # Gestionnaire d'exceptions DRF ├── src/ │ ├── share_kernel/ # Shared Kernel (code partagé) │ │ ├── domain/ # Entités, VO, events, exceptions │ │ ├── application/ # CQRS (Command, Query, UseCase) │ │ └── infrastructure/ # Django models, repos, isolation │ │ └── isolation/ # Backends d'isolation (schema, RLS, FK) │ ├── module/ │ │ ├── tenant/ # Tenant Context │ │ │ ├── domain/ # Events, services, VO │ │ │ ├── middleware.py # Résolution du tenant │ │ │ ├── models.py # Tenant, Membership, Invitation │ │ │ └── management/ # Commandes de gestion │ │ ├── identity/ # Identity Context │ │ │ ├── domain/ # Events, OTP service │ │ │ ├── models.py # User, OTPDevice, EmailVerification │ │ │ └── authentication.py # JWT personnalisé │ │ └── authorization/ # Authorization Context │ │ ├── domain/ # ABAC evaluation (PDP) │ │ ├── condition_engines/ # JSON, Cedar, Casbin │ │ ├── permissions.py # PEP (DRF permissions) │ │ ├── pip.py # PIP (résolution d'attributs) │ │ └── models.py # Policy, ApprovalRequest │ └── testing/ # Utilitaires de test └── docs/ # Documentation Sphinx Les 3 Bounded Contexts ====================== Tenant Context -------------- Gère les organisations (tenants), les memberships utilisateur-tenant, les rôles, départements et invitations. Fournit le middleware de résolution du tenant et les backends d'isolation de données. **Responsabilités :** - Cycle de vie du tenant (création, suspension, archivage) - Gestion des memberships et rôles - Isolation des données (schema, RLS, FK partagée) - Provisionnement et migration des schémas - Système d'invitations par email Identity Context ---------------- Gère l'authentification, les utilisateurs et la sécurité multi-facteurs. **Responsabilités :** - Modèle utilisateur personnalisé (email comme identifiant) - Authentification JWT avec claims tenant - OTP/2FA (Email, TOTP RFC 6238, codes statiques) - Vérification d'email - Changement de tenant (switch tenant) Authorization Context --------------------- Implémente le contrôle d'accès basé sur les attributs (ABAC) et les workflows d'approbation. **Responsabilités :** - Moteur ABAC complet (PDP, PEP, PIP, PAP) - Moteurs de conditions pluggables (JSON, Cedar, Casbin) - Workflows d'approbation avec quorum - Politiques statiques système - Décorateurs ``@abac_required`` Flux d'une requête HTTP ======================== .. code-block:: text Client HTTP │ ▼ Django Middleware Stack │ ├── SecurityMiddleware ├── SessionMiddleware ├── CommonMiddleware ├── CsrfViewMiddleware ├── AuthenticationMiddleware (JWT → User) ├── TenantMiddleware ◄── Résolution du tenant │ ├── Extrait tenant_id du JWT ou header X-Tenant-ID │ ├── Valide que le tenant est actif │ ├── Vérifie la membership de l'utilisateur │ ├── Active le backend d'isolation │ └── Set contextvars (tenant, user, membership) │ ▼ DRF View ├── ABACPermission.has_permission() ◄── Évaluation ABAC │ ├── PIP résout les attributs │ ├── PDP évalue les politiques │ └── Décision: allow / deny / approval_required │ ▼ Réponse HTTP Bus d'événements domaine ========================= Les événements domaine permettent le découplage entre bounded contexts. Le bus supporte deux modes : - **Synchrone** : handlers exécutés immédiatement dans la même transaction - **Asynchrone** : handlers dispatchés via ``transaction.on_commit()`` + Celery .. code-block:: python from src.share_kernel.infrastructure.event_bus import publish_event from src.module.tenant.domain.events import TenantCreated # L'événement est publié après le commit de la transaction publish_event(TenantCreated( tenant_id=str(tenant.id), tenant_slug=tenant.slug, schema_name=tenant.schema_name, ))