Never Trust the Client for a Field the Server Should Own
Naming a controller and its DTOs, and the one question that settles every field in a request DTO: can the client actually be trusted to supply this, or does the server have to own it?
Everything up to this point has been the class diagram and the schema. This is where it turns into a real, working use case: booking a ticket, coded end to end with a framework doing the heavy lifting.
Naming a Controller After the Entity It Acts On
A request to book a ticket has to land somewhere, and the default rule of thumb is simple: name a controller after the entity it performs CRUD operations on. Booking a ticket reads and writes a Ticket, so TicketController is the natural home for it.
That rule isn't absolute. Some actions don't map cleanly onto one entity's usual create-read-update-delete operations, and an API path like /book might deserve its own controller instead of forcing itself into /ticket. Which one a real team picks is mostly a matter of company convention, not a fixed law.
Name a DTO After the Exact Method It Belongs To
The method itself is bookTicket, taking a request and returning a response. The DTOs should carry that same specific name, not just the entity's: BookTicketRequestDTO and BookTicketResponseDTO, rather than a generic TicketRequestDTO that could mean anything.
The Real Question Behind Every Field: Who Controls It?
Building the request DTO isn't a matter of copying every field off the Ticket entity. For each one, the real question is who's allowed to control it: the client sending the request, or the server handling it.
Walk Ticket's fields through that test one at a time:
- Amount. Never from the client. Letting a client name its own price makes the entire booking free to set.
- Time of booking. Never from the client either — the server sets this the moment the request actually arrives.
- Seats. The client can't hand over full
Seatobjects, only the IDs of the seats it wants. Everything else about those seats already lives in the database. - The user and the show. Same pattern: an ID for each, not the full object. The server looks up everything else it needs from that ID.
- Payment. Not included in this request at all, for a reason worth its own section.
The client only ever supplies what it's actually in a position to know: which seats, which show, who's asking. Everything else, the server computes, sets, or looks up on its own.
Sending only an ID instead of a full object isn't just about who controls the field, either. A client might not even be written in the same language as the server, so there's no guarantee it can construct a matching object at all. And a full object costs more to send over the network than a single ID does, adding size and latency for data the server already has anyway.
A Ticket Exists Before It's Ever Paid For
Payment doesn't belong in this request because the Ticket object already exists before payment happens at all. The moment someone starts checking out, a ticket gets created in a pending state, whether or not they ever actually pay.
Anyone who's shopped online has already seen this pattern. Add something to a cart on any real e-commerce site, get to the payment page, then abandon it. An email still arrives: an order was created, its payment is pending. The order exists the instant checkout starts, and if payment never completes, that order just gets cancelled behind the scenes later.
The exact same shape applies here. A ticket is created at the start of checkout, sitting in a pending or processing status, well before a payment succeeds or fails. Whatever happens with the payment afterward is a separate step that updates a ticket already sitting in the database, not something the initial request needs to carry.
One detail worth remembering for later, without going deep into it now: that same ticket ID can double as an idempotency key once payments and retries enter the picture, a concept that belongs to a later stage of this design, not this one.
What Actually Comes Back in the Response
The response needs enough for a user to know what they're being asked to pay and for what. A ticket ID to reference the booking, the amount actually due, and the auditorium's details all belong here, since the client walked in only knowing which seats and which show it asked for.
Keep reading