Refactors on your TicketFlow. Each one backed by tests (28 formalizes the method). Don't look at solutions.md before submitting.
Exercise 1 — The fat view, operated on
- Take your reservation view (or the longest one you have) and list every line with its layer: HTTP (parse/status), domain (rules/transactions), persistence (raw ORM).
- Extract
services/reservations.py::reservar()with the signaturereservar(user, event_uuid, seat_refs, *, clock, gateway) -> Reservation. The view becomes: serializer + call + response. Measure: how many lines does the view have now? - Verify the cotton test: call it from
manage.py shellwithout a request. Did it work?
Exercise 2 — Injected clock
- Create the
ClockProtocol +SystemClock+FakeClock(fixed=...)and use it inreservar()(the production default untouched). - Write the test "a reservation with FakeClock at 23:59 expires at 00:09" without sleeps or freezegun: only the fake.
- Bonus: the expiration job (when 29 arrives) will consume the same Clock — note how it helps.
Exercise 3 — L of Liskov with gateways
- Define the Protocol
PaymentGateway.charge(amount, intent) -> ChargeResult. ImplementFakeGateway(approves if the amount < 1000) andSimulatedGateway(for production-staging). - Break it on purpose: an implementation that returns
Noneon error instead of raising. Write the test that catches it (which assertion failed?). - Document the exact contract: what it returns, what it raises, what idempotency guarantee it gives (14). Is your docstring enough or is strict typing needed?
Exercise 4 — O with a method registry
- Implement
PAYMENT_METHODS: dict[str, type[PaymentMethod]]with the@register("gift_card")registry. Add "cash_on_delivery" (simulated) WITHOUT touching the payments service: only a new module + registration. - What happens in your old code if tomorrow they add "crypto"? How many lines change? (expected answer: 1 new file).
Exercise 5 — I in data access
- Split
AvailabilityReader(what the public endpoint uses) fromReservationWriter(what the checkout uses). Which methods were superfluous in the single interface you imagined first? - Your
utils.py(if you have one): reorganize it by domain (utils/time.py,utils/money.py...) or confess you don't have it yet and write how you would avoid it.
Submit
Paste the view's before/after, the FakeClock tests and the method registry. Next: Lesson 25 — Service patterns.