Request a table
The public form validates event and seating choices before creating a UUID-tracked pending reservation.
FiredOrder up · service night
Guests request a table without an account, staff review and approve from a filtered queue, and a token-secured REST API exposes the same operations downstream.
This split shaped the whole system: public intent first, protected operations second.
The brief
A reservation platform serves two very different rooms. Guests want a seat with zero friction. Staff want control, filters, and a record of every call. The job was to let both work the same ticket without handing the wrong controls to the wrong person. I owned the multi-module architecture, the auth and validation layers, and the dining room model; Matheus built the event and reservation flows and the email confirmation service.
Browse events and seatings, request a table, and get a confirmation code. No account, no friction.
Sign in, filter the queue by event and status, assign a table, then approve or deny. Every call is logged.
Palette
Portfolio cream and charcoal hold the page together, while cinnamon, coffee, apricot, and blue-grey accents add the restaurant tone.
Service choreography
The public form validates event and seating choices before creating a UUID-tracked pending reservation.
FiredEmailService sends the guest a formatted confirmation with event, seating time, and confirmation code.
SentThe dashboard narrows reservations by event and status so staff can work the next service window.
QueuedAuthenticated users review full guest details and attach a valid dining table before approval.
PlatedThe separate REST app uses bearer tokens and OpenAPI docs for third-party access.
ServedThe surfaces
Guests reserve without an account, staff work the queue behind a login, and integrators read a documented API. Different rooms, the same domain underneath.
Guest surface
The guest flow keeps attention on event, seating, and contact details. It behaves like a host stand: collect the request, confirm the code, then move the ticket into the queue.
Staff surface
Staff do not need every record at once. They need the next decision. Event and status filters turn the reservation list into a working queue.
Security surface
The login screen marks the boundary between public browsing and operational authority. That boundary matters because approvals change the guest experience.
Integration surface
Swagger turns the backend into something reviewable. The API is secured with JWT bearer tokens and kept separate from the Thymeleaf web app so future clients, a mobile app or a partner system, can consume the same domain without touching the staff interface.
Console // Seating Stand
Interact with the reservation pipeline to see how guest requests, seating validation, and table assignments coordinate in real-time.
Architecture
01 Clients
02 Delivery apps
03 Domain modules
04 Shared foundation
05 Persistence
Technical Mise en Place
Runtime, framework, and module boundaries.
Module // common-domain · common-infrastructureStaff login and token-secured API access.
Module // auth-domain · auth-applicationReservation, seating, menu, and event data.
Module // restaurant-persistenceStaff screens, responsive UI, and API documentation.
Module // app-web · app-restaurant-rest-apiBuilt for service
Maven multi-module project with shared common libraries. Each module (restaurant, auth, common) has its own Domain, Application, Infrastructure, and Presentation layers with strict dependency inversion.
Custom ValidationService pattern per entity with Result/ValidatedResult wrapper types, field-level error binding to BindingResult, and dedicated validation services for Events, Seatings, Menus, MenuItems, DiningTables, and Reservations.
Seating CRUD with table assignments (many-to-many via SeatingTableEntity), capacity tracking, overlapping seating conflict detection, and cascade delete management.
InitializeDataServiceImpl seeds the database with default dining tables, events, seatings, menus, and menu items on first startup, enabling immediate demo readiness.