HS

Hasara Research Guide

Your final year research project, explained from the beginning. Enter the password to open it.

It only asks once on this device.

5 Implementation
Chapter 5

Implementation and Designing

What you actually built, how it works inside, and why every choice was forced by the evidence rather than by taste.

What this chapter is for

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

Every choice is justified by evidence, not by taste

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

PartChoiceThe 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.

NoTableWhat it holds
1profilesWho someone is, and whether they are an organiser or a traveller
2propertiesThe hotel, guesthouse, villa or resort, owned by one profile
3eventsOne event at one property, with a capacity ceiling and a status of draft, published or cancelled
4resource_typesTickets, 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
5bookingsOne booking by one traveller on one event, with its status and its QR token
6booking_itemsOne line per resource booked, with the unit price copied in at the moment of booking
Database schema diagram showing six tables with keys and relationships
Figure 5.1. The remaining_quantity column on resource_types is the one every availability screen reads, and the only thing that writes it is the booking function.

Why table 4 is the thesis written as a schema

The answer to "why one table?"

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.

One small detail that shows care

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.

TableWho can readWho can write
profilesYourself, plus guests who hold a booking on one of your own eventsOnly your own row
propertiesAnyoneOnly the owner
eventsAnyone, if published. The owning organiser sees their own in any stateOnly the owning organiser
resource_typesAnyone, if the parent event is publishedOnly the organiser who owns the event
bookingsThe traveller who made it, and the organiser whose property it is onOnly through the booking function
booking_itemsSame as the parent bookingOnly through the booking function
Why this matters for the research, not just for safety

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.

An honest note in your own thesis

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
What that looks like in practice

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.

The EVENTIA inventory editor showing ticket, room and add-on resources on one event
The inventory editor. Ticket, room and add-on on one event, in one list. Remaining quantity is read-only, because only the booking function may write it.

3. Atomic multi-resource booking

This is the feature with the greatest technical risk and the clearest correctness requirement.

The bug this prevents

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.

Sequence diagram of multi-resource booking with concurrency control
Figure 4.11. The shape that matters: the client makes one call to a transactional function, instead of a read followed by a write.

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.

The degraded mode, which is a design decision worth quoting

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.

Availability board before a booking, reading 18

Before. The add-on row reads 18.

Availability board after a booking committed elsewhere, reading 16

After, with no page reload. A booking made in a separate session moved it to 16.

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:

  1. Unknown, or belongs to another eventRefused, with the reason shown.
  2. Already checked inRefused as a replay, and the original check-in time is displayed so staff can see when it was first used.
  3. Valid and confirmedAdmitted. Status set to checked in, timestamp recorded, and the booking contents shown so staff know what the guest has paid for.
The by-product that closes the loop

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.

The check-in console refusing an already used token
The check-in console refusing a replay, showing the original check-in time.

What the system was seeded with

6
properties, all 4 types
14
events, 12 of them published
42
resource types across all 3 kinds
288
existing bookings

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.

Say this out loud

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.