El dominio puro de TicketFlow, con el costo a la vista. Sin solutions.md hasta entregar.
Ejercicio 1 — La auditoría hexagonal
- Dibuja (ASCII) tu arquitectura actual con los anillos hexagonales: dominio / aplicación / puertos / adaptadores. ¿Qué flecha apunta HACIA FUERA del dominio (¿el Model en el servicio? ¿settings importado en services?).
- Los puertos: lista los Protocols de tu repo (Clock, Gateway, Repos...): ¿faltan puertos (¿el email? ¿el rate limiter del 23?)? Añade el que falte y la implementación fake del 33.
- La regla de las flechas:
grep -rn "import.*models" services/ygrep -rn "from django" domain/— el segundo debe ser vacío tras el curso. El primero: decide qué queda (¿el patrón activo-record de la 00b?) y documéntalo.
Ejercicio 2 — Los value objects
- Implementa
MoneyySeatRef(§2) con sus invariantes y el test unitario puro (sin Django, la 33): la moneda distinta lanza, el asiento inválido lanza, la suma correcta. - El VO del negocio que falta:
Percentage(la comisión del org, 0-30% con validación) oTimeRange(el evento con start<end). Implementa el que uses y migra UN servicio a él. - El costo de los VOs: mide el impacto en el serializer del 25: ¿el DTO acepta Money o lo convierte? ¿Dónde vive la conversión (¿en el mapper, en el serializer?) y por qué NO en el dominio?
Ejercicio 3 — El agregado Reservation
- Extrae
Reservational dominio puro (§3): la máquina de estados (00b) en la clase, elconfirmar()/expirar()/cancelar(motivo)con las transiciones como métodos, sin Django. - El mapper bidireccional (adapters/mappers.py) y el repositorio del 25 ajustado:
get()devuelve DOMINIO (con los seats cargados: el agregado completo),save()persiste desde el dominio. ¿Qué pasó con el lazy-loading? ¿Elprefetch_relateddel repo es ahora obligatorio? Documenta la consecuencia. - El test del dominio: la suite unitaria del agregado corre SIN BD:
pytest tests/unit/domain -qen <1 s. ¿Cuántos tests migraste del integrado al unitario puro? ¿Cuáles SE QUEDAN en integración (¿el lock? ¿el outbox?)?
Ejercicio 4 — La decisión DDD
- Escribe el ADR "DDD parcial en TicketFlow" (48): qué extrae (VOs, agregado Reservation), qué queda (CRUD en ORM), el costo medido (los mappers, las semanas), y el trigger del DDD completo (¿el segundo servicio del 53? ¿las reglas > N?).
- El contrafáctico: estima (49) el DDD COMPLETO para tu repo: ¿cuántas semanas? ¿Qué ganaría de verdad? ¿Qué líneas del modelo del 51 pagarían esa factura? La respuesta honesta: ¿vale o no vale HOY?
- La frontera con el 53: ¿qué módulos del monolito modular serían los "bounded contexts" (¿tickets vs payments vs events)? Dibuja el mapa de contextos con su lenguaje propio (el ubicuo por contexto): ¿"reserva" significa lo mismo en payments que en tickets?
Ejercicio 5 — El lenguaje ubicuo
- La auditoría del lenguaje: lista los nombres de tu código que NO hablan el negocio (¿
XDataModel? ¿process_stuff?) y re-nómbralos al idioma del dominio. El test: el dueño del negocio (51: el pitch) lee los nombres de tus clases y entiende la mitad — ¿la entiende? - El Anti-corruption layer: el payload de la pasarela (o del webhook del 17): ¿dónde se traduce
settled→PaymentSucceeded? Si el vocabulario ajeno llegó al dominio (¿unif status == "settled"en services?), muévelo al adaptador. - El glosario: crea
docs/reference/lenguaje.mdcon las 15 palabras del dominio (reserva, expira, confirma, tarjeta regalo, comisión...) y su definición exacta de UNA línea — el vocabulario compartido entre el negocio y el repo (el doc del 48 que el 51 prometió).
Entrega
Pega el anillo hexagonal, los VOs con tests, el agregado sin Django con su mapper y el ADR de DDD parcial. Después: Lección 53 — Monolito modular frente a microservicios.