Railway reservations: one seat, several journeys a day

Published . By the DiagramDesk team.

Seat 41 in coach B is not one thing a railway sells. On the train that leaves on Monday it can be sold to someone going three stops and again to someone boarding where the first passenger gets off; on Tuesday it is on sale all over again. A railway model is right or wrong according to whether it can say that.

The dataStretchesCancellingUse casesData flowClassesMistakes

The data: train, run, stop, seat

A train here is a service with a number, such as the 07:40 from one city to another. It calls at a list of stations in order, and each call is a Stop with a sequence number and a departure time. The train runs on many days, and each day's running is a Run: that is what passengers are booked onto, because Monday's train and Tuesday's are different journeys with different people on them.

A booking belongs to one run and records where the travellers board and where they leave, as two stops. It covers one or more passengers travelling together, each with their own seat, and each seat is left empty while that passenger is on the waiting list. A passenger is numbered within the booking, so the booking's reference is part of the passenger's key and that line is solid, an identifying relationship. Every other line is dashed: the seat is only a foreign key on the passenger, and the other entities have ids of their own.

Booking points at two stops: where the stretch starts and where it ends.calls atis served inruns asis made up ofhasis booked onis boarded atis left atcoversis given toTRAINstring train_no PKstring nameSTOPint stop_id PKstring train_no FKstring station_code FKint sequencetime departsSTATIONstring code PKstring nameRUNint run_id PKstring train_no FKdate runs_onCOACHint coach_id PKstring train_no FKstring classSEATint seat_id PKint coach_id FKstring seat_noBOOKINGstring reference PKint run_id FKint board_stop FKint leave_stop FKstring statusint wait_position "empty once confirmed"PASSENGERstring reference PK, FKint passenger_no PKstring nameint seat_id FK "empty while waiting"
Booking points at two stops: where the stretch starts and where it ends.Open in the editor

Two relationships run between Stop and Booking, one for boarding and one for leaving, and that is allowed: the same two entities can be related in more than one way, as long as each relationship has its own name. The coach and its seats hang off the train rather than the run in this version, on the assumption that a service always has the same make-up. A railway that changes coaches from day to day would move them to Run, and that is the kind of assumption to state in a report.

Selling a seat for part of the way

Because a booking has a boarding stop and a leaving stop, a seat is taken only for that stretch. Seat 41 held from stop 2 to stop 5 is free from stop 5 onwards, and free before stop 2. The check is the same overlap test a hotel uses for nights, with stop sequence numbers in place of dates: two stretches clash when each starts before the other ends. A passenger leaving at stop 5 and another boarding at stop 5 do not clash.

Getting this wrong is expensive in a real railway and visible in an assignment. A model that books a seat "on the train" sells it once per day, however far its passenger actually travels.

Cancelling, and what it sets off

The booking sequence is the one everybody draws. The cancellation is more instructive, because a cancelled booking hands its seats on. The sequence below cancels a two-person booking, confirms the refund to the passenger, and then offers the freed stretch to the waiting list, longest waiting first.

The loop checks waiting bookings in order; the opt frame runs only if one fits.loop [Waiting bookings, longest waiting first]opt [A waiting booking fits]Cancel booking K7Q2Cancel K7Q2Free seats 41 and 42 from stop 2 to stop 5Cancelled, refund dueCancelled, refund on its waySeats free from stop 2 to stop 5Do the free seats cover its stretch?Yes or noConfirm it with those seatsConfirmedPassengerBooking appBookingsSeat mapWaiting list
The loop checks waiting bookings in order; the opt frame runs only if one fits.Open in the editor

Two frames do different jobs here. The loop walks the waiting list; the opt frame, an optional section with a single guard, confirms a waiting booking only if one fits the freed seats. A waiting booking for stops 1 to 4 does not fit seats freed from stop 2, and is passed over, which is why the check asks about the stretch and not just the seat count. The passenger who cancelled gets their answer before any of that runs: they should not wait while the system reshuffles other people's seats.

Use cases

Passengers search, book, check their booking and cancel, online or at a counter, where a booking clerk does the same things on their behalf. The clerk has one job the passenger never sees: printing the list of passengers for each coach before the train leaves.

Joining the waiting list extends booking: it happens only when a train is full.Railway reservation system«include»«extend»Search trainsCheck bookingstatusBook ticketsCancel a bookingPrint thepassenger listPay for ticketsJoin the waitinglistPassengerBooking clerkPayment provider
Joining the waiting list extends booking: it happens only when a train is full.Open in the editor

The booking clerk and the passenger share Book tickets and Cancel a booking, which is correct: two actors can take part in the same use case. Drawing the clerk as a specialised passenger would be wrong, because the clerk books for other people and does not travel on the ticket.

Data flow

The level 1 DFD splits the work into finding seats, booking, cancelling with reallocation, and loading the timetable. The timetable in D1 has one writer, process 4.0, which loads each new timetable from the planning office; the booking processes only read it. Leave 4.0 out and D1 becomes a store that is read but never written, one of the first things a marker looks for.

The booking processes only read D1; process 4.0 is its one writer.stations, datetrains callingseats takenchoiceschoice, travellerspayment requestpayment resultbookingseats givencancellationwaiting bookingsseats movedrefund noticenew timetabletrains, stopsPassengerPlanning office1.0Find seats2.0Book3.0Cancel and reallocate4.0Load timetableD2 Seat allocationsPayment providerD1 TimetableD3 Bookings
The booking processes only read D1; process 4.0 is its one writer.Open in the editor

Classes

The class diagram puts the stretch logic on Run, as freeSeats(board, leave), because only a particular day's running knows which seats are taken. Each run keeps its own waiting list, drawn as composition. A train has at least two stops, which the multiplicity 2..* says precisely: a service that calls nowhere else cannot carry anyone anywhere.

Booking owns its passengers; a passenger has a seat, or none while waiting.1 · calls at · 2..*1 · runs as · 0..*1 · carries · 0..*1 · covers · 1..*0..* · sits in · 0..11 · keeps · 1Train+trainNo: String+stops: List<Stop>+stopAt(station) StopStop+sequence: int+departs: TimeRun+runsOn: Date+freeSeats(board, leave) List<Seat>Seat+seatNo: String+coachClass: StringBooking+reference: String+status: String+cancel() RefundPassenger+name: StringWaitingList+add(booking)+offer(seats, board, leave)
Booking owns its passengers; a passenger has a seat, or none while waiting.Open in the editor

The usual errors

  1. Seats attached to the train, not the day's run, so Monday's booking blocks Tuesday.
  2. A seat booked for the whole route when the passenger rides two stops.
  3. Origin and destination as columns on Train, which loses every stop in between.
  4. One passenger per booking, so a family travelling together gets four unrelated references.
  5. The waiting list as a separate table of copied details, instead of a status and a position on the booking itself.
  6. A cancellation that ends at the refund, leaving the freed seats unoffered.

Your brief may name a limit on passengers per booking, fare classes, or concessions for some travellers. Each is a rule on one of the entities above, not a new structure, and the report should say where each rule lives.