Generated from: “Who does what with the ticketing system — customers, agents, the payment provider — and which features depend on others?”
View the Markdown
```meta
title: Ticketing system — actors and feature dependencies
subtitle: Who does what with the ticketing system, and which feature needs which.
tag: DRAFT
```
```callout
tone: note
title: Assumptions
body: "Three actors: the customer buys and manages seats, the support agent handles refunds and reissues, and the payment provider (Stripe) authorizes and refunds card charges. Event organizers and venue staff are out of scope. Every purchase is paid by card; there is no invoice flow."
```
## Actors and their use cases
The customer never touches money directly. Every purchase includes a card payment that the payment provider completes, and the provider is the only actor that can confirm or fail a charge. The support agent approves refunds and reissues tickets, but never creates a purchase.
```usecase
system: Ticketing
actors:
- { id: customer, name: Customer, kind: person, side: left }
- { id: agent, name: Support agent, kind: person, side: left }
- { id: psp, name: Payment provider, kind: system, side: right }
cases:
- { id: browse, name: Browse events }
- { id: reserve, name: Reserve seats }
- { id: buy, name: Buy tickets }
- { id: pay, name: Pay by card }
- { id: confirm, name: Confirm charge }
- { id: refund, name: Request refund }
- { id: approve, name: Approve refund }
- { id: reverse, name: Reverse charge }
- { id: reissue, name: Reissue ticket }
- { id: transfer, name: Transfer ticket }
links:
- customer -> browse
- customer -> reserve
- customer -> buy
- customer -> refund
- customer -> transfer
- agent -> approve
- agent -> reissue
- psp -> confirm
- psp -> reverse
relations:
- { from: buy, to: reserve, kind: include, label: holds seats 10 min }
- { from: buy, to: pay, kind: include }
- { from: pay, to: confirm, kind: include }
- { from: refund, to: approve, kind: extend, label: over 200 EUR }
- { from: approve, to: reverse, kind: include }
- { from: reissue, to: buy, kind: extend, label: lost or damaged }
```
Refunds under 200 EUR run without an agent. Above that line the request waits for an approval, and the charge reversal runs only after the approval.
## Which feature depends on which
The dependency order is the order features can ship. Seat reservation works without payment, so a free-event mode is possible. Payment does not work without reservation: a charge with no held seat cannot be fulfilled. Refunds sit at the top of the stack and need every module below them.
```table
columns: [Feature, Needs, Owner actor, Status]
rows:
- [Browse events, "—", Customer, { v: Live, tone: pos }]
- [Reserve seats, Browse events, Customer, { v: Live, tone: pos }]
- [Buy tickets, "Reserve seats, Pay by card", Customer, { v: Live, tone: pos }]
- [Pay by card, Confirm charge, Payment provider, { v: Live, tone: pos }]
- [Transfer ticket, Buy tickets, Customer, { v: Beta, tone: warn }]
- [Request refund, "Buy tickets, Reverse charge", Customer, { v: Live, tone: pos }]
- [Approve refund, Request refund, Support agent, { v: Live, tone: pos }]
- [Reissue ticket, "Buy tickets, Approve refund", Support agent, { v: Planned, tone: muted }]
note: "The Needs column lists direct dependencies only. Transitive ones follow from the rows above."
```
## Module dependencies in the codebase
Each feature above lives in one module. Dependencies point downward: `refunds` and `tickets` use `payments`, `payments` imports the provider client through a port, and nothing depends upward on `refunds`. A change to `catalog` reaches every module; a change to `refunds` reaches none.
```pkg
title: Ticketing modules
dir: TB
packages:
- { id: refunds, col: 1, row: 1, name: refunds, contains: [request, approval, reversal] }
- { id: tickets, col: 2, row: 1, name: tickets, contains: [purchase, transfer, reissue] }
- { id: payments, col: 2, row: 2, name: payments, contains: [charge, confirm, reverse] }
- { id: seating, col: 1, row: 2, name: seating, contains: [hold, release, allocate] }
- { id: catalog, col: 1, row: 3, name: catalog, contains: [events, venues, prices] }
- { id: psp, col: 2, row: 3, name: psp-client, contains: [stripe-adapter], stereotype: external }
deps:
- { from: refunds, to: tickets, kind: use, label: reads the purchase }
- { from: refunds, to: payments, kind: use, label: requests reversal }
- { from: tickets, to: seating, kind: use, label: holds and allocates seats }
- { from: tickets, to: payments, kind: use, label: charges the card }
- { from: seating, to: catalog, kind: import, label: reads seat maps }
- { from: payments, to: catalog, kind: import, label: reads prices }
- { from: payments, to: psp, kind: import, label: port only }
```
The `psp-client` package is the only place the provider SDK appears. Swapping the provider changes one adapter and no use case.