Implementation and Designing
What you actually built, how it works inside, and why every choice was forced by the evidence rather than by taste.
Chapter 5 documents what you actually built, not what you planned to build. That distinction matters, because a chapter describing a feature that does not exist is the fastest way to lose credibility in a viva.
Pages 87 to 103.
The rule this chapter follows
Nowhere in this chapter does it say "I chose Next.js because I like it". Every single technology is justified against a constraint that Chapter 4 established from the data.
If an examiner asks why you chose anything, your answer always has the same shape: because the evidence said X, and this was the option that satisfied X.
What was built with, and why
| Part | Choice | The constraint it answers |
|---|---|---|
| Language | TypeScript | An availability number read as the wrong type is a whole class of bug. Static typing removes it before the code runs. |
| Website framework | Next.js | Fast first load on a mobile connection, which NFR-01 requires under 2 seconds. And both audiences are served from one codebase over one data model, which is what lets them read identical information. |
| Styling | Tailwind CSS | Keeps the responsive work next to the markup, for the 3 screen widths NFR-07 requires. |
| Database | Supabase, managed PostgreSQL | A booking must change several inventory rows as one indivisible act, which needs a relational database with real transactions. Managed rather than self-hosted, because 55 percent of properties have nobody who could run a server. |
| Accounts and login | Supabase Auth | Password handling is delegated to an audited service instead of written by hand, which answers the security condition 46.7 percent of organisers raised. |
| Live updates | Supabase Realtime | Changes are published by the database itself rather than polled for, so every viewer reads the same row. This is the working form of the H3 result. |
| Hosting | Vercel | No per-property licence cost, answering NFR-08 and the cost sensitivity in 40 percent of adoption answers. |
| QR codes | Generated in the browser, scanned with the phone camera | Validation runs on a phone at the door with no scanner hardware, which suits the small front-of-house teams the data describes. |
The two choices worth defending out loud
PostgreSQL over a document store
Document databases are a common choice for event platforms. But a booking has to decrement several independent resource counts and either succeed completely or leave no trace, and that needs transactional guarantees a document store does not give you by default.
Chapter 2 also found that platforms on eventually consistent stores show users stale availability. Stale availability is the exact failure this research exists to fix.
Logic pushed into the database
The booking logic does not live in the website code. It lives inside the database as a function the website calls.
That means correctness does not depend on how many copies of the website are running, or on the browser behaving. It is enforced in one place, by the thing that owns the data.
The database, six tables
Deliberately small. One design decision carries the entire research, and it is the fourth table.
| No | Table | What it holds |
|---|---|---|
| 1 | profiles | Who someone is, and whether they are an organiser or a traveller |
| 2 | properties | The hotel, guesthouse, villa or resort, owned by one profile |
| 3 | events | One event at one property, with a capacity ceiling and a status of draft, published or cancelled |
| 4 | resource_types | Tickets, rooms and add-ons in ONE table, separated only by a kind column. Each row has a price, a total quantity and a remaining quantity |
| 5 | bookings | One booking by one traveller on one event, with its status and its QR token |
| 6 | booking_items | One line per resource booked, with the unit price copied in at the moment of booking |
Why table 4 is the thesis written as a schema
Tickets, room allocations and add-ons like meals, drinks and parking mean different things to a guest. But they behave identically to the system. Each has a price, a total, a remaining count, and each has to be decremented safely when booked.
Putting them in one table gives three things three tables cannot:
One query returns the complete resource position for an event, so the traveller's page and the organiser's dashboard read the same rows rather than assembling a picture from three places. One live subscription keeps every screen consistent. One locking rule governs every decrement, so the correctness argument has to be made once instead of three times.
And the negative case is the real argument. Splitting them into three tables would have fragmented availability across three code paths and rebuilt, inside the system, the exact inconsistency between information sources that the research identifies as the problem.
booking_items stores the unit price as it was at the moment of booking, rather than pointing at the live price.
So if the organiser raises the price next week, a booking already taken keeps the value it was actually sold at. It is a small thing, but it is the kind of detail an examiner asks about to see whether the design was thought through.
Access control, enforced by the database
Instead of the website hiding things people should not see, the database itself refuses to hand them over. This is called row level security.
| Table | Who can read | Who can write |
|---|---|---|
profiles | Yourself, plus guests who hold a booking on one of your own events | Only your own row |
properties | Anyone | Only the owner |
events | Anyone, if published. The owning organiser sees their own in any state | Only the owning organiser |
resource_types | Anyone, if the parent event is published | Only the organiser who owns the event |
bookings | The traveller who made it, and the organiser whose property it is on | Only through the booking function |
booking_items | Same as the parent booking | Only through the booking function |
One. A bug in the website cannot leak one property's bookings to another, because the website is not what is withholding them.
Two. The guarantee can be tested without going through the screens, which is what test case TC-09 does. A rule that only exists in the interface passes a screen test while leaving the data wide open.
Three. It is a direct answer to the data. Security appeared in 46.7 percent of substantive organiser adoption answers, ranking third among all conditions.
The first version of the profiles rule was too tight. Letting a user read only their own row is correct for guests, but it left organisers unable to put a name to a booking they had received, so every booking read simply as "Guest".
The rule was widened to include guests who hold a booking on that organiser's own events. Deliberately narrow: it exposes exactly the guests who chose to book with that organiser and nobody else. Three extra checks were added to TC-09 to hold that boundary, because a rule widened once can be widened carelessly again.
The four features that are the contribution
Chapter 5 documents these four and deliberately skips routine things like registration and login, because those introduce no logic specific to this research.
1. The unified information layer
This is not a component you can point at on a screen. It is the property that the event's schedule, price, inclusions and live remaining availability form one record, read identically by the guest deciding whether to come and by the organiser deciding how much to buy.
Three decisions make it real:
- The one inventory table, so availability lives in one place
- The remaining count is only ever written by the booking function, so it cannot drift away from the bookings that produced it
- Both screens subscribe to the same table, rather than to separate copies of it
When an organiser answers a guest asking what an event includes and what is left, the figure they read is the same row the guest's phone is reading.
Organiser item 18, which said answering that question currently means checking with somebody else first, returned a mean of 3.875, the third highest of any organiser item. Item 17, that a centralised view would improve decisions made during an event and not only while planning, returned 3.900.
2. The multi-resource inventory model
An organiser defines any number of resources on an event, each with a kind, a name, a price and a total quantity. Remaining starts equal to total, and after that only a booking changes it.
One validation rule protects a guarantee rather than a field: you may reduce a total, but never below the quantity already booked. Allowing that would create an oversell through configuration instead of through concurrency. The rule runs inside the same transaction as the update.
Events also carry a capacity ceiling that applies across all ticket-kind resources, which is the space and safety limit organiser item 13 identified.
3. Atomic multi-resource booking
This is the feature with the greatest technical risk and the clearest correctness requirement.
The obvious way to write a booking is: read the availability, decide whether it is enough, then write the new number.
Between the read and the write, somebody else can book. Your decision was made on a number that is already out of date, and now two people have the last ticket.
On an event night, where lots of guests book the same event within a few minutes, this is not a remote possibility. It is the expected case.
So the check and the decrement happen inside one database transaction, called as a single operation. The website never reads availability and then writes it.
create_booking(event, items) returns booking begin transaction -- claim every resource row involved, in ascending id order select * from resource_types where id in (the requested resources) order by id for update -- check each requested quantity against the claimed figures for each item: if remaining_quantity < requested quantity: raise INSUFFICIENT_STOCK(name of that resource) -- check the event capacity ceiling across ticket-kind resources if already booked tickets + requested tickets > event.capacity: raise CAPACITY_EXCEEDED(the capacity) -- commit the whole booking, or none of it for each item: update resource_types set remaining_quantity = remaining_quantity - quantity insert into bookings (with a random qr_token) insert into booking_items (one row per item, price copied in) return booking commit
Why for update
It stops any other transaction from touching those rows until this one finishes. That is what removes the gap between checking a number and changing it.
Why ascending id order
Two bookings wanting overlapping baskets in different orders could each end up holding what the other needs, and freeze forever. That is a deadlock. Always claiming rows in the same order makes it impossible.
Why the error names the resource
So the booking panel can mark the one line that failed rather than throwing away the whole basket. It came from a real traveller theme about booking mechanisms failing.
4. Live availability propagation
The organiser dashboard and the traveller event page both subscribe to changes on the inventory table, filtered to the events in view. When a booking commits, the database publishes the row change and every subscribed screen gets the new number, with no polling and no refresh.
The subscription runs from the browser straight to the database service, rather than through the website's own server, so the update does not wait on an extra round trip.
If the live channel is unavailable, the page falls back to asking periodically and displays an indicator that the figures may lag.
Silently showing stale numbers with no warning would reproduce precisely the failure the research identified, and would be worse than showing them with a warning attached.
QR validation at check-in
Every confirmed booking carries a token behind its QR code. It is a long cryptographically random value, not a sequential number and not a scramble of the booking id. A guessable token would let somebody get in without a booking, which is the ticket fraud problem Chapter 2 identified.
At the door, staff scan or type the token, and there are exactly three outcomes:
- Unknown, or belongs to another eventRefused, with the reason shown.
- Already checked inRefused as a replay, and the original check-in time is displayed so staff can see when it was first used.
- Valid and confirmedAdmitted. Status set to checked in, timestamp recorded, and the booking contents shown so staff know what the guest has paid for.
Recording check-in timestamps produces attendance data. An organiser who can compare bookings taken against guests actually admitted finally has the number that organiser items 9 and 11 said was missing.
That is the coordination problem closing on itself: the system that takes the bookings is also the system that tells you who turned up.
What the system was seeded with
Seeded with partial availability rather than full stock, on purpose. A system tested only against untouched inventory never reaches the boundary conditions that matter. And no real person's name, phone number or email appears anywhere in it, which ties back to the ethics commitments in Chapter 3.
If they ask you
Why did you put tickets, rooms and add-ons in one table?
Because they differ in what they represent to a guest but behave identically to the system. Each carries a price, a total quantity and a remaining quantity, and each must be decremented atomically when booked.
One table produces three properties the alternative does not: a single query returns the complete resource position for an event, a single realtime subscription keeps every surface consistent, and a single locking discipline governs all concurrent decrements.
Modelling them separately would have fragmented availability across three code paths and reintroduced, inside the system, the same inconsistency between information sources that the research identifies as the primary failure of the manual arrangements it replaces.
Why PostgreSQL rather than MongoDB or another document store?
Because a booking must decrement several independent resource counts and either succeed completely or leave no trace, which requires transactional guarantees a document store does not provide by default.
Chapter 2 identifies concurrency control over multi-resource inventory as the central algorithmic problem for platforms of this type, and one reviewed source specifically finds that platforms operating on eventually consistent stores expose stale availability to users. Stale availability is the exact failure this research exists to eliminate.
Why is the booking logic in the database instead of in your application code?
So that correctness does not depend on client behaviour or on the number of running application instances. If the check and the decrement were done by the website, two copies of the website running at once could each make a correct-looking decision on stale data.
Inside the database, the rows are locked for the duration of the transaction, so there is no window between checking a figure and changing it. The client makes one call and either gets a booking or gets a named refusal.
Why is your access control in the database rather than in the interface?
Two reasons. First, a defect in the interface then cannot expose one property's booking data to another, because the interface is not what withholds it. Second, it makes the guarantee testable independently of the user interface, which is what TC-09 does by issuing requests directly against the API with one user's credentials against another user's data.
It also answers something the data asked for. Security and data protection appeared in 46.7 percent of substantive organiser adoption responses, ranking third among all conditions.
What is not documented in this chapter, and why?
Account registration, authentication and profile management. They are implemented using standard patterns and introduce no logic specific to this research, so documenting them would add pages without adding anything checkable.
The four features that are documented are the ones that constitute the technical contribution: the unified information layer, the multi-resource inventory model, atomic booking, and live availability propagation.
Chapter 5 documents what was actually built. Every technology was justified against a constraint the data produced rather than against preference. PostgreSQL because a booking must be transactional across several rows, and managed services throughout because 55 percent of properties have no dedicated staff and cost ranked as a deciding adoption factor. The schema realises the unified information layer as a physical property rather than a presentation convention, by generalising tickets, rooms and add-ons into a single inventory table with a single availability column. Access control sits in row level security policies so the guarantee holds independently of the interface. Four contribution features are documented, and routine functionality like login is deliberately excluded.