CQRS en Django : read model dénormalisé sans Event Sourcing

CQRS en Django : read model dénormalisé sans Event Sourcing

Les quatre articles précédents de la série ont posé les briques pour qu’un système distribué reste cohérent : Saga pour orchestrer des workflows, Outbox pour publier des événements fiables, Inbox pour les consommer sans doublon, Idempotency Keys pour protéger l’API. Il manque une question : que fait-on des événements une fois publiés ? La réponse la plus fréquente est : on les utilise pour construire des vues de lecture. C’est exactement ce que propose le pattern CQRS (Command Query Responsibility Segregation) : séparer le modèle d’écriture du modèle de lecture, quand les deux divergent suffisamment pour que les forcer dans une même structure coûte plus cher que les dédoubler. ...

5 juin 2026 · 8 min · Anthony
Idempotency Keys : empêcher un client de payer deux fois

Idempotency Keys : empêcher un client de payer deux fois

Les deux articles précédents ont traité l’idempotence côté événement : le pattern Outbox garantit qu’un message est publié au moins une fois, et le pattern Inbox garantit qu’il n’est consommé qu’une fois. Reste un troisième endroit où le même problème se pose, plus en amont : l’API HTTP elle-même. Quand un client lance un POST /api/payments et que sa connexion lâche avant de recevoir la réponse, il ne sait pas si le paiement a été créé ou non. S’il retry, il risque de payer deux fois. S’il ne retry pas, il risque de ne pas payer du tout. Le pattern Idempotency Key, popularisé par Stripe et adopté depuis par la plupart des APIs de paiement, résout ce dilemme en mettant le contrôle du retry dans les mains du client. ...

4 juin 2026 · 8 min · Anthony
Pattern Inbox : consommer des événements sans les rejouer deux fois

Pattern Inbox : consommer des événements sans les rejouer deux fois

L’article précédent sur le Transactional Outbox a posé une garantie claire : tout événement écrit en base finira par être publié. Cette garantie est volontairement at-least-once. Un consommateur peut recevoir le même événement deux fois, trois fois, ou plus si le réseau se comporte mal. Le pattern Outbox ne promet jamais l’unicité. La conséquence est immédiate : si le consommateur fait deux fois l’effet du message, il facture deux fois, envoie deux emails, débite deux fois le stock. La cohérence garantie côté producteur s’effondre côté lecteur. ...

3 juin 2026 · 7 min · Anthony
Transactional Outbox : publier des événements sans perdre la cohérence

Transactional Outbox : publier des événements sans perdre la cohérence

Quand un service modifie sa base et veut prévenir le reste du système d’envoyer un événement à Kafka, RabbitMQ ou SQS, le code naïf ressemble à ça : on écrit en base, puis on publie. Si la publication échoue après la commit, l’événement est perdu. Si la publication réussit mais que la commit échoue, l’événement parle d’un état qui n’existe pas. Ces deux cas sont la définition du dual-write problem. ...

2 juin 2026 · 8 min · Anthony
Pattern Saga : gérer les transactions distribuées sans rollback

Pattern Saga : gérer les transactions distribuées sans rollback

Une opération métier qui touche plusieurs services pose un problème que SQL résout depuis cinquante ans à l’intérieur d’une base unique : que faire quand une étape réussit et que la suivante échoue ? Tant que tout vit dans la même base, BEGIN ... ROLLBACK suffit. Dès qu’on appelle un service externe, une API tierce ou une autre base, ce filet de sécurité disparaît. Le pattern Saga répond à cette question. Plutôt que de tenter une transaction ACID impossible, il découpe l’opération en étapes locales, chacune accompagnée d’une transaction compensatrice qui sait défaire son effet. Si l’étape 4 échoue, on rejoue en sens inverse les compensations des étapes 1, 2 et 3. ...

1 juin 2026 · 8 min · Anthony
Permissions déclaratives en DRF avec rest_access_policy

Permissions déclaratives en DRF avec rest_access_policy

Les permissions dans Django REST Framework fonctionnent, mais elles montrent leurs limites dès que les règles d’accès deviennent un peu complexes. Plusieurs rôles, des objets appartenant à un utilisateur, des actions custom sur un ViewSet : on se retrouve rapidement avec des classes has_permission et has_object_permission qui mélangent des vérifications hétérogènes, difficiles à lire et encore plus difficiles à tester. rest_access_policy (paquet djangorestframework-access-policy) propose une autre approche : déclarer les règles d’accès sous forme de statements, à la manière des politiques IAM d’AWS. Le résultat est lisible en un coup d’oeil, testable indépendamment du ViewSet, et extensible sans réécrire toute la classe. ...

26 mai 2026 · 7 min · Anthony
Hash, HMAC et chiffrement : sécuriser un token Django

Hash, HMAC et chiffrement : sécuriser un token Django

Une comparaison == sur un hash ne suffit pas à choisir le bon mécanisme. sha256, HMAC, hash salé, chiffrement : chaque approche offre des garanties différentes. Comprendre lesquelles change concrètement la façon de stocker et vérifier un token en Django. Hash simple import hashlib token_hash = hashlib.sha256(token.encode()).hexdigest() Un hash simple est déterministe : la même entrée produit toujours la même sortie. Aucun secret serveur n’est impliqué. Il est impossible de retrouver le token original depuis le hash (sha256 est une fonction à sens unique). Mais si quelqu’un connaît ou devine le token, il peut recalculer le hash et comparer. ...

25 mai 2026 · 4 min · Anthony
Django : save() ne valide pas — le cycle complet de validation

Django : save() ne valide pas — le cycle complet de validation

Appeler obj.save() après avoir défini des validators et un clean() sur le modèle laisse croire que la validation est garantie. Elle ne l’est pas. Django ne déclenche pas full_clean() lors d’un save(), et ce comportement est délibéré. Comprendre pourquoi change la façon d’architecturer la validation dans un projet. Ce que save() fait réellement Le cycle de vie d’un save() est plus court que ce qu’on imagine : Signal pre_save envoyé field.pre_save() appelé sur chaque champ (auto_now, auto_now_add, etc.) INSERT ou UPDATE SQL selon la présence d’un pk Signal post_save envoyé Aucune validation n’y figure. Ni vérification de blank, ni max_length, ni appel à clean(). C’est aussi vrai pour Model.objects.create() et Model.objects.bulk_create() : les trois méthodes persistent sans valider. (bulk_create() est traité en détail dans Django in_bulk() et bulk_create() si vous travaillez sur des insertions en masse.) ...

18 mai 2026 · 5 min · Anthony
Timing attacks en Django avec constant_time_compare

Timing attacks en Django avec constant_time_compare

Une comparaison == sur un token paraît anodine. En pratique, elle laisse filtrer une information mesurable : le temps d’exécution varie selon le nombre de caractères corrects. C’est le principe d’une timing attack, et c’est suffisant pour qu’un attaquant reconstitue le token caractère par caractère. Le problème : la comparaison qui s’arrête trop tôt Python compare les chaînes caractère par caractère et s’arrête dès qu’une différence est trouvée. def verify_token(request): user_token = request.GET.get('token') valid_token = "secret_abc123" if user_token == valid_token: return JsonResponse({'access': 'granted'}) return JsonResponse({'access': 'denied'}) Concrètement : ...

14 mai 2026 · 3 min · Anthony
Vues matérialisées vs cache Django pour les requêtes lentes

Vues matérialisées vs cache Django pour les requêtes lentes

Le réflexe face à un endpoint de reporting lent, c’est souvent le cache. Un @cache_page, un cache.set(), et le problème disparaît… jusqu’à la prochaine expiration. Cette approche a une limite structurelle que les vues matérialisées PostgreSQL résolvent à la racine. Le problème du cache sur les endpoints analytiques Le cache Django stocke le résultat d’une vue Python. La requête SQL coûteuse s’exécute quand même à chaque expiration du cache. Pour un rapport construit sur plusieurs JOINs et agrégations, ça signifie que le premier utilisateur après chaque cache miss attend plusieurs secondes. ...

13 mai 2026 · 4 min · Anthony

Newsletter

Reçois les nouveaux articles directement dans ta boite mail.

Pas de spam. Désabonnement en un clic.