10 KiB
Online Booking Application
Cliff Hill's Coding Project
This is a full-stack application simulating an online booking system for conference rooms.
Design Decisions
React Contexts for State Management
- Global state for rooms, bookings, and users via context providers.
- Real-time updates using SSE in context.
Modular Component Architecture
- Reusable, focused components (e.g., BookingForm, RoomList, RoomSelect, CalendarView).
- Shared UI logic abstracted into dedicated components.
TypeScript for Safety and Documentation
- Strong typing for all props, state, and data models.
- Centralized type/interface definitions.
Unified Color and Style Logic
- Room/event colors assigned by room id modulo, using CSS custom properties.
- Consistent visual theming across lists, dropdowns, and calendar.
Separation of Concerns
- Business logic and API calls abstracted into helpers and service modules.
- Thin UI components focused on rendering and interaction.
Robust Error Handling and Validation
- Field-level validation and user-friendly error messages in forms.
- Backend errors formatted for clarity.
Centralized Logging
- Logging utility used throughout frontend and backend for analytics and debugging.
- Key actions and lifecycle events are logged.
JSDoc and Docstrings for Documentation
- File-level and component-level documentation in both frontend and backend.
- TypeScript interfaces and Python models are documented.
React Router for Navigation
- Page routing and programmatic navigation after booking actions.
Accessibility and User Experience
- Disabled states, error messages, and skeleton loaders for better UX.
- Visual distinction for selected/unavailable rooms.
FastAPI Backend with SQLAlchemy ORM
- Async API endpoints grouped by resource (rooms, bookings, users).
- Database models with relationships and constraints.
Pydantic Schemas for Validation
- Separate schemas for create, update, and response objects.
Service Layer Abstraction in Backend
- Business logic separated from HTTP routing.
Testing and Code Quality
- Pytest, coverage, pre-commit hooks, and code style enforcement for backend; run
pytestfrom the backend folder. - Jest and React Testing Library, code style enforcement and additional pre-commit hooks for frontend; run
yarn run jestfrom the frontend folder. - All tests can be run from the
noxcommand in the root directory, or for tests specific to each part of the project, runnoxin eitherfrontendorbackendrespectively.noxfrontend tests include:pre-commit,coverage,audit,storybook, andjest.noxbackend tests include:pre-commit,safety,mypy,tests,typeguard,xdoctest, anddocs-build.
nox could very easily be integrated into a CICD pipeline for full validation of the code to be done before anything could be merged into main.
Docker Compose for Orchestration
- Multi-service setup: backend (FastAPI), frontend (React), and Postgres database.
- Custom network, healthchecks, and persistent volumes.
- Environment variables managed via .env files.
Extensibility and Maintainability
- Modular design for easy feature addition and refactoring.
- Generated from Hypermodern Python Cookiecutter for best practices.
AI Use
I used AI to stub out a couple of files:
- The backend/Dockerfile and frontend/Dockerfile - to speed up the process of getting docker loaded efficiently for the project.
- The compose.yml file - getting the different images gathered together quickly.
- The compose.dev.yml file - used to get a further understanding of how to hook up an extension to the previous file.
- The mermaid diagrams used in this file.
- I have the CodeGPT plugin in VSCode, and it has helped with docstrings, logging messages, and sometimes reducing the time it takes me to write out the code.
- I used AI to rapidly set up tests for each users component (router, service), then adapted it to the others independently. I believe AI is great for getting reasonable tests written quickly, and then I simply refined it, and replicated the kinds of tests across the different components.
- I was experimenting with some AI edits for debugging the tests.
- My frontend skills are less polished than my backend skills. I've been using AI along the lines of how I would use StackOverflow, answering questions to help me get the code written.
- AI was very helpful in getting the frontend to do what I wanted quickly.
- I used AI to help clean up and better structure the CSS for the frontend project, as well as get some code documentation in place.
- AI was used to refactor code a few times to make it cleaner and better organized.
- AI was used for debugging the tests to quickly correct them, for frontend and backend.
Development Environment Setup
Follow these steps to get your local development environment running:
1. Clone the repository
You can use either HTTPS or SSH:
HTTPS:
git clone https://github.com/xlorepdarkhelm/numinar-coding-project.git
cd numinar-coding-project
SSH:
git clone git@github.com:xlorepdarkhelm/numinar-coding-project.git
cd numinar-coding-project
2. Copy environment variables
cp .env.example .env
# Or, if present:
cp .env.sample .env
3. Install backend dependencies
cd backend
poetry install
4. Set up pre-commit hooks (recommended)
pre-commit install
5. Install frontend dependencies
cd ../frontend
yarn install
6. Start Docker Compose in development mode (in a separate terminal)
From the project root:
docker compose -f compose.dev.yml up
This will start the backend (port 8000), frontend (port 3000), and Postgres (port 5432) with hot-reload enabled for rapid development.
7. Run tests (backend & frontend)
Backend tests
cd backend
nox # Run all tests and checks (also builds API docs)
# or run only unit tests:
nox --session=tests
API Documentation
- The
noxcommand above will also build the backend API documentation. - View the API docs in development mode: Open http://localhost:8000/docs in your browser (when running dev Docker Compose).
- Generated static docs (after running nox): See
backend/docs/build/index.htmlrelative to the project root.
Frontend tests
cd frontend
nox
# or run only nox tests:
nox --session=jest
All tests
from the project root
nox
Note - you can use any of the sessions from frontend or backend here to isolate just that test.
Further improvements
Integration testing, and end-to-end tests really would make this robust. Having all of the tests run in CICD before allowing code to be merged/commited to the main branch would be a mechanism to help ensure code quality. I would have set up the github project to have "feature branches" be made, to add whatever feature that a work item/issue had, and then Peer Reviews - typically set up with 2 peers reviewing 1 PR and approving it, aswell as all CICD checks/tests needing to pass before allowing the branch to be merged.
I would have templates in place for creating a PR, with a set of instructions that would give the "definition of done" - a checklist that would need to be completed before the issue could be marked as completed and a PR could then be reviewed.
Further kinds of tests can be added for the frontend through the frontend's noxfile.
Diagrams
Docker Compose Components
graph TD
subgraph Docker_Network
B[Frontend]
C[Backend]
D[PostgreSQL]
C -- "SQL: 5432" --> D
end
Client[Client] -- "HTTP: 3000" --> B
Client -- "HTTP/REST: 8000" --> C
Database Schema Diagram
classDiagram
class User {
string email
string name
}
class Room {
int id
string name
string location
string equipment
int capacity
}
class Booking {
int id
int room_id
datetime start_time
datetime end_time
string title
}
class Invitee {
int id
int booking_id
string user_email
}
User "1" <|-- "*" Invitee : user_email
Room "1" <|-- "*" Booking : room_id
Booking "1" <|-- "*" Invitee : booking_id
Component Diagram
graph TD
subgraph Frontend
F1[Pages] --> F2[Components]
F2 --> F3[API Calls]
end
subgraph Backend
B1[FastAPI Routers] --> B2[Pydantic Schemas]
B1 --> B3[Service Layer]
B3 --> B4[SQLAlchemy Models]
B4 --> B5[PostgreSQL]
end
F3 -->|REST| B1
Backend Request Data Path
sequenceDiagram
participant Frontend
participant FastAPI_Router as FastAPI Router
participant API_Endpoint as API Endpoint
participant Pydantic_Schema as Pydantic Schema
participant Service_Layer as Service Layer
participant DB_Model as DB Model
participant Database
Frontend->>FastAPI_Router: HTTP Request (REST)
FastAPI_Router->>API_Endpoint: Route Match
API_Endpoint->>Pydantic_Schema: Validate & Parse Input
Pydantic_Schema-->>API_Endpoint: Validated Data
API_Endpoint->>Service_Layer: Pass Data
Service_Layer->>DB_Model: Business Logic
DB_Model->>Database: DB Query/Update
Database-->>DB_Model: Query Result
DB_Model-->>Service_Layer: Data
Service_Layer-->>API_Endpoint: Response Data
API_Endpoint-->>Frontend: HTTP Response (JSON)
SSE Update Flow (Real-Time Room Availability)
sequenceDiagram
participant Frontend
participant FastAPI_Router as FastAPI Router
participant SSE_Endpoint as SSE Endpoint
participant Event_Generator as Event Generator
participant Publisher as Publisher
Frontend->>SSE_Endpoint: Open EventSource /availability/stream
SSE_Endpoint->>Event_Generator: Start Async Event Loop
loop While Connected
Event_Generator->>Publisher: Wait for Event or Timeout
Publisher-->>Event_Generator: Room Availability Event | Keep-Alive
Event_Generator-->>Frontend: Send SSE Event (data: ...)
end
Frontend-->>SSE_Endpoint: Disconnect (close EventSource)
SSE_Endpoint-->>Event_Generator: Cleanup Subscriber