Ressource
Attention à la traduction
L’Eventual Consistancy se traduit par Cohérence Finale car, à la fin de la saga, les données SONT cohérentes entre elles, soit parce que tout s’est bien passé, soit parce qu’il y a eu compensation afin de maintenir, in fine, lesdites données
Remember that we try to identify aggregates i.e we’re actively looking for areas, islands that need to be immediately consistent in an ocean of eventual consistency. […] And maybe that’s one of the biggest things with DDD, the revelation that a model doesn’t really need to always be immediately consistent, like in the CRUD approach; some parts need it, others don’t. 1
- Chaque agrégat reste toujours valide en interne
- Les agrégats ne sont pas forcément cohérents entre eux immédiatement
- La cohérence globale est atteinte plus tard (eventual consistency)
Exemple
Règle métier
- Une Commande peut être créée immédiatement
- La réservation de stock se fait asynchronement
- Pendant un court laps de temps, une commande peut exister sans stock réservé : C’est acceptable pour le métier
Agrégat 1 : Order (Commande)
public class Order {
private final UUID orderId;
private final UUID productId;
private final int quantity;
private OrderStatus status;
public OrderCreatedEvent createEvent() {
return new OrderCreatedEvent(orderId, productId, quantity);
}
public void markAsConfirmed() {
this.status = OrderStatus.CONFIRMED;
}
public void markAsRejected() {
this.status = OrderStatus.REJECTED;
}
}
Agrégat 2 : Inventory (Stock)
public class Inventory {
private final UUID productId;
private int availableQuantity;
public boolean reserve(int quantity) {
if (availableQuantity >= quantity) {
availableQuantity -= quantity;
return true;
}
return false;
}
}ApplicationService
public class OrderService {
public void placeOrder(UUID productId, int quantity) {
Order order = new Order(UUID.randomUUID(), productId, quantity);
orderRepository.save(order); // transaction locale
eventBus.publish(order.createEvent()); // async
}
}public class InventoryEventHandler {
public void on(OrderCreatedEvent event) {
Inventory inventory = inventoryRepository.findByProductId(event.productId());
Order order = orderRepository.findById(event.orderId());
if (inventory.reserve(event.quantity())) {
inventoryRepository.save(inventory);
order.markAsConfirmed();
} else {
order.markAsRejected();
}
orderRepository.save(order);
}
}Ce qui est temporairement incohérent (et acceptable)
| Moment | Commande | Stock |
|---|---|---|
| t0 | CREATED | Non réservé |
| t1 | CONFIRMED | Réservé |
Avec deux commande en même temps
Situation de départ
- Stock du produit P = 5
- Deux commandes arrivent quasi simultanément
- Commande A : quantité 3
- Commande B : quantité 3
t0 – Réception des commandes
Order A → CREATED
Order B → CREATED
Stock → 5
t1 – Publication des événements
OrderCreatedEvent(A)
OrderCreatedEvent(B)
Les événements partent dans le bus (Kafka, RabbitMQ, etc.)
- Aucun ordre garanti
- Possibilité de traitement concurrent
t1 - Traitement séquentiel
A traité en premier puis B
Stock = 5
Reserve 3 → OK
Stock = 2
Order A → CONFIRMED
Stock = 2
Reserve 3 → FAIL
Order B → REJECTED
=> Acceptable si le métier l’accepte