← Retour aux travaux
2023 · SaaS workforce-management · mission CTO

Exiqtive

Un SaaS de workforce-management multi-tenant — back-end Django + DRF, front-end React, infrastructure AWS gérée par Terraform. Nous tenons le poste de CTO depuis 2023, en architecturant les trois couches et en faisant grandir l'équipe d'ingénierie dans le système.

Rôle
CTO · architecture · back-end · front-end · infra
Durée
Depuis 2023, en cours
Équipe
1 CTO Hazenfield
VISUEL DE PROJET · PLACEHOLDER
FIG. 01
Contexte

Exiqtive (aussi appelé Clear Intent) est un SaaS de workforce-management pour les équipes opérationnelles distribuées — comptes, employés, structure organisationnelle, badges et checkpoints, scorecards de performance, et un signal de readiness qui cascade de bas en haut dans l'arbre organisationnel. Multi-tenant, authentification OAuth2, temps réel via WebSocket, avec quarante-deux workflows de notification qui relient le tout aux personnes concernées.

Nous tenons le poste de CTO depuis le démarrage du projet en 2023. Cela voulait dire architecturer les trois couches en même temps — back-end sur Django + DRF + Celery + Channels, front-end sur React avec un design system ancré dans Storybook, infrastructure sur AWS géré par Terraform — d'abord seuls, puis au fur et à mesure qu'une équipe d'ingénieurs venait se greffer.

La forme choisie était délibérée. Une couche service, pas des vues bavardes. Des cascades asynchrones via Celery avec `transaction.on_commit`, pour que l'étape suivante ne s'exécute qu'une fois la base de données d'accord. Une isolation multi-tenant imposée au niveau de la ligne par le RLS de Postgres, pas seulement par la couche applicative. Des scopes OAuth2 qui verrouillent les endpoints. Une surface API v1 / v2 en parallèle pour que les clients ne perdent jamais un contrat. Des tests assez rapides pour tourner à chaque push — deux mille deux cent, en deux minutes trente.

Deux ans et demi plus tard, la forme architecturale tient toujours. Un ingénieur DevOps dédié a fini par reprendre l'opérationnel quotidien de l'infrastructure que nous avions mise en place ; l'architecture, les frontières entre apps, la taxonomie des notifications, les investissements de test — tout cela reste à nous pour évoluer.

Périmètre

Ce que nous avons bâti.

Architecture (3 couches)01

Back-end, front-end et infrastructure AWS architecturés comme un seul système cohérent, avant que l'équipe ne grandisse pour s'y intégrer.

common02

App Django cœur : comptes, employés, structure organisationnelle, gestion de contenu, notifications. ~275 migrations.

proficiency03

Treillis badge / checkpoint / responsabilité — le cœur du modèle de readiness. 61 Ko de signal handlers ; ~194 migrations.

performance04

KPIs, périodes de performance, scorecards.

assignments05

Traitement asynchrone des assignations — pas de modèles, uniquement des tâches Celery ; processus de relève avec verrous Redis et suivi de progression.

OAuth2 + scopes06

django-oauth-toolkit avec scopes read / write / admin par famille de ressources ; 8 classes de permission DRF empilées au-dessus.

Multi-tenant via RLS07

Politiques row-level security de Postgres + RLSMiddleware. La fuite de tenant s'arrête à la base de données.

API v1 / v2 en parallèle08

Les contrats clients vieillissent à leur rythme — les deux versions sont citoyennes de première classe du code et de la suite de tests.

Couche WebSocket09

Django Channels avec connexions tenant authentifiées OAuth2 ; le routage supporte /ws/account/<id>/invites/.

Taxonomie de notifications10

42 workflows Knock définis en code, utilisés dans la cascade et la couche activité.

Infrastructure de tests11

2 220 tests ; `--keepdb --parallel N` ; broker / channel / cache en mémoire pour l'environnement de test. 13 min série → ~2,5 min en parallèle.

Estate AWS Terraform12

VPC, ALB, ECR, bastion, SES, RDS, multi-environnement (dev / staging / prod / v1). Opérationnellement porté par un DevOps dédié aujourd'hui ; nous continuons de revoir.

Approche

À quoi ressemble le travail, en 4 pièces.

01

Architecte sur trois couches

Tenu le poste de CTO dès le premier jour. Back-end sur Django + DRF + Celery + Channels, front-end sur React avec un design system ancré dans Storybook, infra sur AWS géré par Terraform (VPC, ALB, ECR, bastion, SES, RDS, multi-environnement). Les trois sont sortis de la même main, délibérément, pour que les coutures entre elles soient là où nous l'avons décidé et non là où l'organigramme les avait laissées. Un ingénieur DevOps dédié a plus tard repris l'opérationnel ; la forme architecturale reste.

02

Multi-tenant par la ligne, pas par l'app

L'isolation tenant est imposée au niveau de Postgres par le RLS, pas seulement par la couche applicative. Une classe de permission mal configurée ne peut pas accidentellement fuir entre comptes — la base refuse. RLSMiddleware positionne la variable de session avant chaque requête, les scopes OAuth2 verrouillent les endpoints, et huit classes de permission s'empilent au-dessus.

03

Cascades asynchrones qui respectent la DB

Le readiness cascade de bas en haut sur cinq niveaux de l'arbre organisationnel (responsabilité → rôle → position assignment → employé → compte). Chaque niveau utilise Celery + `transaction.on_commit` pour que l'étape suivante ne s'allume qu'après commit de l'écriture précédente. Retries avec backoff ; verrous Redis pour empêcher des recalculs concurrents du même compte.

04

Tester vite ou ne pas tester

Une suite de 2 220 tests qui prenait treize minutes en série était une taxe qu'on ne pouvait pas se permettre de payer chaque jour. Nous avons investi dans une infrastructure de tests parallèles : `--keepdb --parallel N`, broker, channel layer et cache en mémoire pour l'environnement de test, password hasher plus rapide. Deux minutes trente sur une machine 8 cœurs. Seuil de couverture 80% imposé en CI.

Choix techniques

Les solutions dont nous sommes le plus fiers.

01

Multi-tenant imposé par RLS

L'isolation multi-tenant vit au niveau Postgres : RLSMiddleware positionne la variable de session avant chaque requête ; les politiques row-level security sur les tables tenantées filtrent automatiquement. Un bug dans une classe de permission ou un filtre de queryset oublié ne peut pas fuir entre comptes — la base arrête. Ceinture et bretelles avec les classes de permission applicatives.

02

Cascade de readiness à cinq niveaux

Le readiness remonte de bas en haut à travers `responsabilité → rôle → position assignment → employé → compte`. Chaque niveau est une tâche Celery qui ne se déclenche qu'après commit de l'écriture précédente, via `transaction.on_commit`. Retries avec `max_retries=3, default_retry_delay=60`. Des helpers comme `trigger_readiness_cascade_from_responsibility(id)` sont les points d'entrée publics ; les signals les appellent, jamais les tâches sous-jacentes directement.

03

Versioning API qui laisse les contrats vieillir

Les clients qui paient pour le contrat v1 ne devraient pas être migrés à notre rythme. Chaque app porte `urls/v1.py`, `urls/v2.py`, `urls/shared.py` plus des arbres parallèles de serializers et viewsets. Une classe `CustomVersioning` lit `?version=` et dispatche. Les deux versions sont citoyennes de première classe de la suite de tests jusqu'à ce qu'un contrat soit retiré.

04

OAuth2 avec permissions DRF gated par scope

django-oauth-toolkit pour l'émission, une classe `OAuth2ScopesPermission` sur chaque viewset, et des scopes découpés read / write / admin par famille de ressources — `employees:read`, `employees:write`, `assignments:recalc`. Les connexions WebSocket s'authentifient de la même manière via `OAuth2TokenAuthMiddleware`. Le front-end ne voit jamais de cookie de session.

05

Tests parallèles, 13 min → 2,5 min

Le premier coût qu'on a coupé : une suite de 2 220 tests à 13 minutes en série. `--keepdb --parallel N` avec N bases clones, `ALWAYS_EAGER` pour Celery en tests, `InMemoryChannelLayer` pour Channels, `LocMemCache` pour le cache, password hasher plus rapide. Sur une machine 8 cœurs, la même suite tourne en deux minutes trente. Seuil de couverture 80% imposé en CI.

06

Estate AWS Terraform, multi-environnement

Quatre environnements déclarés dans un seul arbre Terraform (`clear_intent`, `clear_intent _stg`, `clear_intent _prod`, `clear_intent _v1`) ; ressources globales partagées (ECR, DNS, SES) en tête ; stacks par-env en dessous — back-end, front-end, admin, Storybook chacun géré là. Bastion pour l'accès ad-hoc, ALB pour le trafic, ECR pour les images, RDS pour l'état. Bâti seuls au début ; opérationnellement porté par un DevOps dédié aujourd'hui avec nous toujours en revue.

Résultats

Quelques chiffres, en formes brutes.

2,5+ ans
Mission CTO, en cours
3 couches
Back-end · front-end · infra
2 220
Tests, ~2,5 min en parallèle
42
Workflows de notification Knock

Stack
Django + DRFPython 3.13PostgreSQL 15Redis 7CeleryChannelsOAuth2ReactTerraformAWS

Un projet qui mérite ce niveau de soin ?

Démarrer une conversation