A partir de esta conversación escribo este Gist.
Tenemos un almacen que tiene un stock por producto.
Regla de negocio:
- No podemos vender un producto que no tenemos stock.
En este caso podríamos pensar que esto es una invariante de stock, no puede existir un stock negativo. Por lo que creamos el concepto:
class Stock {
constructor(private readonly productId: string,
private readonly stock: number) {
if (this.stock < 0) throw new Error('stock cannot be negative');
}
}
Hemos creado una invariante en un objeto de dominio. Esto es muy común en DDD de intentar representar las invariantes a nivel de Value Object, Entity, o Aggregate.
Pero, ¿realmente representa el dominio?
No. El concepto de Stock puede pasar muchas cosas:
- Se ha perdido en el almacen el articulo.
- Se ha roto y no se había reportado.
- Dos personas han comprado el mismo articulo a la vez.
- Que entre se ha hecho el pedido y se ha confirmado se ha queado sin stock.
Hay un montón de casos que hacen que un stock pueda ser negativo temporalmente, de ahí la consistencia eventual.
Lo que dice la regla de negocio es que en el momento de compra queremos que el stock sea positivo. Por lo que lo que podemos hacer es tener una regla de negocio que sea en el domain service estilo:
Dream code :pointdown:
class OrderService {
placeOrder(order) {
const stockForProduct = stock.for(order.product);
if (stockForProduct < 0) throw new Error('stock cannot be negative');
}
}
En el segundo caso, hemos implementado la regla de negocio pero permitiendo que el stock sea negativo para poder implementar entonces esa logica de negocio para esos casos. Estilo:
- Si stock es menor que 0, entonces pedir con urgencia a tales proveedores y entregarlo con máxima priodidad y dar un vale de regalo por las molestias.
En el primer caso, el objeto Stock no puede representar stock negativo porque es a nivel de invariante de objeto de dominio. En el segundo si que permitimos esa representación del objeto y podemos modelar el dominio de forma menos rigida.
Buenas! Gran ejemplo :) como siempre
Quiero comentar algo que veo a ver que opinas:
Creo que no puedes tener stock negativo, a no ser que el 3er punto no lo tengas solventado. Puedes tener el stock mal identificado en tu sistema, es algo común por lo que he ido viendo además, el stock en tu dominio se debe sincronizar con el stock del almacen real, y a su vez ese stock del almacen real debe comprobarse de vez en cuando (tenemos 1200 camisetas y en realidad hay solo 1100).
Sumo otro problema que me gustó mucho cuando lo escuché, y es que haces un pedido, se reserva el stock para ti, el almacen lo envía genial y por el camino (suele ser por zonas del valles normalmente :P ), se "cae del camion". Lo que yo entiendo de todo esto, no es dejar de hacer comprobaciones en nuestro dominio, sino también tener en cuenta otras casuísticas muy reales como las que comentas.
En el comando de añadir a carrito se ve algo mas claro la comprobación del stock por ejemplo, la condición de carrera que hay comprobandolo, pero Udi Dahan por ejemplo comentaba que una posible solución es "caducar" el carrito cada X tiempo.
Respecto a tu solución, cuando haces un pedido, no creo que la acción sea preguntar por el stock sino mas bien "hacer una reserva", moviendo así la lógica de stock disponible al aggregate, por ejemplo:
No se si hemos solucionado el tercer punto con esto? ya sería imposible que alguien reserve el mismo producto dos veces en dos transacciones diferentes
Entiendo el punto que quieres comentar, Udi Dahan habla mucho sobre estos temas y sobre la flexibilidad que debe tener nuestro sistema
A ver que opinas y como seguimos!
Muchas gracias Aleix!