Why the Service Still Checks, Even Though the UI Already Did

A UI that hides already-booked seats doesn't make the service's own availability check redundant, and where a thrown exception actually goes once it leaves the service.

September 1, 20261 min read39 / 43

A real question worth taking seriously: if the UI already stops a user from selecting a seat that's already locked, why does bookTicket bother checking availability all over again?

A UI Reflects a Moment, Not a Guarantee

The UI's picture of which seats are free is only ever a snapshot from the moment that page loaded. Two people can open the exact same seat-selection screen at the exact same instant, both see the same seat marked available, and both go on to request it, each one having no idea the other exists.

This is the identical race condition this series worked through in detail earlier, back when the seat-locking algorithm was first designed. The UI check happening earlier in the flow doesn't make the service's own check redundant — it's the only check that's actually happening at the moment two requests can genuinely collide. Whatever the UI already filtered out has no bearing on what two concurrent requests might both still believe is true.

An Exception Thrown in the Service Doesn't Go to the Client As-Is

Once bookTicket throws, that exception surfaces in the controller, not directly on the wire. The controller decides what the client actually sees, which might be the same message, or might be reshaped into something more appropriate for an external caller than a raw internal exception would be.

That's one more real job a controller does hold, alongside its basic structural checks: catching what a service throws and deciding how much of it is fit to hand back out.