Cerrada

Entry 002 - Casos de uso, guardas de estado, event bus y contexto Payment

dddarchitectureinventorysalespaymenteventsrefactortesting

Consolidacion de puertos de stock

Los tres puertos granulares (ReserveStock, ReleaseStock, ConfirmStock) y sus respectivos adaptadores se consolidaron en un unico HandleStockPort con tres operaciones: reserveStock, releaseStock y commitStock. En Inventory, el use case HandleStockForSale implementa las tres operaciones. Esto reduce de 6 archivos a 2 sin perder expresividad.

Eliminacion de QuantityVO

Se elimino QuantityVO por sobreingenieria: solo envolvia un number con un getter y una validacion minima, sin comportamiento de dominio real. Se reemplazo por number directo con validaciones en los puntos correctos:

El constructor de SaleItem se hizo private, forzando el uso del factory method create().

Renombramiento de estados y campos

Se renombraron conceptos para reflejar mejor el lenguaje del dominio:

Guardas de transicion de estado en Sale

Se identifico que Sale no protegia sus transiciones de estado. Ahora las tres operaciones sensibles solo se permiten desde DRAFT:

CreateSale valida que no exista una venta con el mismo ID.

Fail-fast en AddItemToSale

AddItemToSale ahora verifica el estado de la venta antes de reservar stock. Esto evita reservar inventario innecesariamente cuando la venta ya no acepta operaciones. Si getProductInfo falla despues de reservar, se hace rollback del stock.

Propagacion de errores de dominio

Se realizo una auditoria que revelo multiples puntos donde errores de dominio se silenciaban o no se propagaban. Los cambios:

Todos los use cases de Sales ahora verifican que getSaleById no retorne undefined, retornando SaleNotFoundError en vez de explotar con getValue()!.

Errores de dominio exportados

Se extrajeron los errores que estaban como clases privadas dentro de los archivos de dominio a domain/Errors/, haciendolos importables:

Validaciones en Product

Correccion de bugs en repositorios e infraestructura

Mejoras al sistema de tests

Event Bus y evento SalesConfirmed

Se implemento un bus de eventos en memoria (InMemoryEventBus) como singleton, con interfaces EventBus y EventHandler<T> en shared/domain/bus/. Al confirmar una venta, RegisterSale publica un evento SalesConfirmed con saleId y totalAmount. Este evento es consumido por el contexto Payment para crear automaticamente una orden de pago.

Nuevo bounded context: Payment

Se agrego el contexto Payment para gestionar ordenes de pago asociadas a ventas confirmadas:

Reglas de dominio en PaymentOrder

Comunicacion entre contextos

La comunicacion Sales → Payment se da a traves de eventos de dominio, manteniendo los contextos desacoplados. Sales publica SalesConfirmed sin conocer a Payment. Payment se suscribe al evento y reacciona creando la orden de pago.

Otros cambios

Test Results

Sales (Iter 2)

Venta completa con multiples productos y confirmacion cross-context

5 passed
Create sale
Behavior cross context when register sale, all products stock decreases
Trying to cancel a completed sale should return a controlled domain error
Behavior cross context when add items to other sale, all products stock decreases
Cancel current sale should release reserved stock and restore stock to state before this sale

Payment & State Guards (Iter 2)

Creacion de payment order via evento, pagos parciales/exactos/cash, fail-fast en ventas no-draft

10 passed
Confirming a sale publishes SalesConfirmed and creates a PaymentOrder
Adding item to confirmed sale fails fast without reserving stock
Adding item to cancelled sale fails fast without reserving stock
Cancelling a confirmed sale returns a domain error
Confirming a cancelled sale returns a domain error
Partial payments with cash overpayment calculates correct change
Non-cash payment that exceeds total returns domain error
Exact payment completes the order with zero change
Adding payment to a completed order returns domain error
Adding payment to non-existent order returns PaymentOrderNotFoundError

DDD Artifacts

Payment

PaymentOrder

Aggregate Root Payment

Properties

id UuidVO
saleId UuidVO
totalAmount PriceVO
payments Payment[]
status PaymentOrderStatus
change PriceVO
createdAt Date
completedAt Date?

Methods

create()addPayment()getChange()getStatus()getTotalAmount()

Invariants

Estado inicial siempre es PENDING Solo pagos CASH pueden exceder el total (genera cambio) Pagos no-cash que excedan el total son rechazados No se aceptan pagos si la order ya esta COMPLETED Transiciona a COMPLETED cuando los pagos cubren el total

Payment

Entity Payment

Properties

id UuidVO
method PaymentMethod
amount PriceVO
status PaymentStatus
createdAt Date

Methods

create()getAmount()getMethod()

Invariants

Metodo debe ser CASH, CARD o TRANSFER Monto debe ser positivo

PaymentMethod

Value Object
CASH | CARD | TRANSFER

PaymentOrderStatus

Value Object
PENDING | PARTIAL | COMPLETED | CANCELLED | FAILED

PaymentStatus

Value Object
PENDING | COMPLETED | FAILED

Shared Domain

PriceVO

Value Object Shared
Non-negative Max 2 decimal places Stored as cents add() substract() multiply()

Result<E, T>

Value Object Shared
ok() | fail() Fuerza verificacion de isSuccess antes de getValue()

Ports & Adapters

Port
HandleStockPort
Reservar, liberar y confirmar stock en el contexto Inventory
Adapter
HandleStock (infrastructure)
Port
GetProductsInfo
Obtiene nombre y precio de productos del contexto Inventory
Adapter
GetProductsInfo (infrastructure)