https://github.com/ibalosh/parking-lot
https://github.com/ibalosh/parking-lot
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/ibalosh/parking-lot
- Owner: ibalosh
- Created: 2025-10-11T07:28:18.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-11-15T08:52:07.000Z (8 months ago)
- Last Synced: 2025-11-15T10:19:05.594Z (8 months ago)
- Language: Ruby
- Size: 137 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# 🅿️ Parking Lot Management System
[](https://github.com/ibalosh/parking-lot/actions/workflows/ci.yml)
A RESTful API for managing a parking lot with X amount of spaces.
## Requirements
- Ruby version in `.ruby-version` file
- Rails 8+
- Bundler
- SQLite3
## Setup
```bash
bundle install
bin/rails db:setup
```
Once the setup is complete, it will see the test data too.
You can start the Rails server:
```
rails server
```
## Running Tests
```bash
bundle exec rspec
```
## Documentation
- **[API Endpoints](docs/API_ENDPOINTS.md)** - Detailed endpoint documentation with request/response examples
- **[Database Schema](docs/DATABASE.md)** - Entity relationship diagram and table descriptions
- **[Postman Collection](docs/Parking-Lot.postman_collection.json)** - Import this collection to test the API in Postman
## Architecture
### Models
- **ParkingLotFacility** - Manages parking lot capacity (for this specific case - 54 spaces)
- **Ticket** - Parking tickets with barcode, status (active/returned), and timestamps
- **Payment** - Payment records linked to tickets
- **Price** - Configurable pricing (€2/hour)
- **Currency** - Multi-currency support
### Key Features
- **Race condition protection**: Pessimistic locking prevents duplicate payments and overbooking
- **Data integrity**: validations and unique constraints
- **Idempotent operations**: Multiple payment attempts return existing payment
- **15-minute grace period**: Payment valid for 15 minutes after purchase
- **Dynamic pricing**: Calculated per started hour based on entry time
## Design Decisions
1. **Pessimistic Locking** - Used in `TicketPaymentService` and `create_ticket!` to prevent race conditions in
high-concurrency scenarios (duplicate payments, overbooking).
2. **Service Layer** - `PaymentService` extracts payment logic from the controller, making it easier to test and
maintain.
3. **Active Tickets Scope** - Only active tickets count toward capacity; returned tickets free up spaces immediately.
4. **Barcode as Identifier** - Tickets identified by barcode instead of database ID for better UX
5. **Separate ID and Barcode Fields** - The tickets table uses both an auto-incrementing `id` (primary key) and a
`barcode` field (unique indexed string). While the barcode could technically serve as the primary key, keeping a
separate integer ID provides several benefits:
- **Performance**: Integer primary keys are more efficient for joins and foreign key relationships (e.g., payments
linking to tickets)
- **Flexibility**: If barcode format needs to change (e.g., from numeric to alphanumeric), it won't affect foreign
key relationships
- **Convention**: Follows Rails and ActiveRecord conventions, making the codebase more maintainable
The barcode serves as a natural key for API consumers and physical interactions, while the ID serves as a technical
key for internal database relationships.
6. **Payment Expiration** - 15-minute window ensures customers don't pay and then park for hours before leaving.
## Design Notes & Assumptions
### Database & Multi-tenancy
- **Single Parking Lot**: Currently, the API uses the first parking lot facility from the database. This can be easily
extended to support multiple parking lots by accepting a facility ID
- **Database Choice**: SQLite for simplicity and ease of setup. Production deployments should use PostgreSQL or MySQL.
### Pricing & Currency
- **Multi-currency Support**: The system supports multiple currencies (Currency model), but each parking lot uses a
single currency defined in its Price configuration. Easily extendable this way
- **Price History**: Tickets capture the price configuration at the time of entry (`price_at_entry`), ensuring
historical accuracy even if pricing changes later.
- **Hourly Pricing**: Every started hour is billed as a full hour. For example, parking for 1 hour and 1 second costs
the same as 2 full hours. This can be made more granular (e.g., 15-minute increments) if needed.
### Payment System
- **Payment Methods**: Stored as denormalized string values (`credit_card`, `debit_card`, `cash`) with validation. No
separate payment method table was created for simplicity. This can be normalized if payment method metadata is needed
in the future.
- **Multiple Payments**: The system allows multiple payment records per ticket (e.g., if payment expires after 15
minutes and customer pays again). The latest payment is used to determine ticket state.
- **Idempotent Payments**: Attempting to pay an already-paid ticket returns the existing payment instead of creating a
duplicate (HTTP 200 instead of 201).
### Ticket Lifecycle
- **Ticket States**: Tickets have two states: `active` (default) and `returned`. Only active tickets count toward
parking capacity.
- **Ticket Return**: The bonus endpoint (PUT `/api/tickets/{barcode}`) allows marking tickets as returned when cars
exit. This immediately frees up a parking space.
- **No Authentication**: As per requirements, no authentication or authorization is implemented. In production, this
would be required.
### API Design
- **Versioning**: All endpoints are namespaced under `/api` to allow for future versioning (`/api/v2`, etc.) possibility.
- **Error Handling**: Consistent error responses with appropriate HTTP status codes (404 for not found, 422 for
validation errors, 503 for parking lot full).