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¶
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)
Flux d’une requête HTTP¶
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
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,
))