TicketFlow's pure domain, with the cost in view. No solutions.md before submitting.
Exercise 1 — The hexagonal audit
- Draw (ASCII) your current architecture with the hexagonal rings: domain / application / ports / adapters. Which arrow points OUTWARD from the domain (the Model in the service? settings imported in services?).
- The ports: list your repo's Protocols (Clock, Gateway, Repos...): are any ports missing (email? 23's rate limiter?)? Add the missing one and 33's fake implementation.
- The arrows rule:
grep -rn "import.*models" services/andgrep -rn "from django" domain/— the second must be empty after the course. The first: decide what stays (00b's active-record pattern?) and document it.
Exercise 2 — The value objects
- Implement
MoneyandSeatRef(§2) with their invariants and the pure unit test (no Django, 33): the different currency raises, the invalid seat raises, the correct sum. - The missing business VO:
Percentage(the org's commission, 0-30% with validation) orTimeRange(the event with start<end). Implement the one you use and migrate ONE service to it. - The VOs' cost: measure the impact on 25's serializer: does the DTO accept Money or convert it? Where does the conversion live (the mapper? the serializer?) and why NOT in the domain?
Exercise 3 — The Reservation aggregate
- Extract
Reservationto the pure domain (§3): 00b's state machine in the class,confirmar()/expirar()/cancelar(motivo)with the transitions as methods, no Django. - The bidirectional mapper (adapters/mappers.py) and 25's repository adjusted:
get()returns DOMAIN (with the seats loaded: the full aggregate),save()persists from the domain. What happened to lazy-loading? Is the repo'sprefetch_relatednow mandatory? Document the consequence. - The domain test: the aggregate's unit suite runs WITHOUT a DB:
pytest tests/unit/domain -qin <1 s. How many tests did you migrate from integration to pure unit? Which ones STAY in integration (the lock? the outbox?)?
Exercise 4 — The DDD decision
- Write the ADR "Partial DDD in TicketFlow" (48): what it extracts (VOs, the Reservation aggregate), what stays (CRUD in the ORM), the measured cost (the mappers, the weeks), and full DDD's trigger (53's second service? the rules > N?).
- The counterfactual: estimate (49) FULL DDD for your repo: how many weeks? What would it really gain? Which lines of 51's model would pay that bill? The honest answer: is it worth it TODAY or not?
- The border with 53: which modules of the modular monolith would be the "bounded contexts" (tickets vs payments vs events?)? Draw the context map with its own language (the ubiquitous per context): does "reserva" mean the same in payments as in tickets?
Exercise 5 — The ubiquitous language
- The language audit: list the names in your code that do NOT speak the business (
XDataModel?process_stuff?) and rename them to the domain's language. The test: the business owner (51: the pitch) reads your classes' names and understands half of them — do they? - The Anti-corruption layer: the gateway's payload (or 17's webhook): where does
settledget translated toPaymentSucceeded? If the foreign vocabulary reached the domain (anif status == "settled"in services?), move it to the adapter. - The glossary: create
docs/reference/lenguaje.mdwith the domain's 15 words (reserva, expira, confirma, tarjeta regalo, comisión...) and their exact ONE-line definition — the vocabulary shared between the business and the repo (48's doc that 51 promised).
Submit
Paste the hexagonal ring, the VOs with tests, the Django-free aggregate with its mapper and the partial-DDD ADR. Next: Lesson 53 — Modular monolith vs microservices.