FR1: User can reserve a parking spot
FR2: User pays for the reservation
FR3: User can park a car on the parking spot
FR4: User can leave before the reservation time expires
FR5: System handles no-shows by charging for the first 24 hours
NFR1: Scalability - Support an international company with 1000s of parking lots across nations
NFR2: High Availability - System must remain operational with minimal downtime
NFR3: Consistency - No double-booking; once reserved, a spot must be guaranteed available
NFR4: Performance - Reservation operations should complete within reasonable time
NFR5: Security - Secure payment processing and user data protection
The storage requirements are well within the capabilities of relational databases, which will help satisfy our consistency requirements (NFR3).
The system will be decomposed into the following core services:
| ServicePrimary ResponsibilityFunctional RequirementsNon-Functional Requirements | |||
| Reservation Service | Manages parking spot reservations, availability checks, and reservation lifecycle | FR1, FR5 | NFR1, NFR3, NFR4 |
| Payment Service | Handles payment processing and integration with third-party payment providers | FR2 | NFR2, NFR5 |
| Transaction Service | Manages vehicle entry/exit operations and updates reservation status | FR3, FR4 | NFR2, NFR3 |
| Transaction Monitor Service | Detects no-shows and handles automatic reservation status updates | FR5 | NFR2, NFR3 |
| User Service | Manages user accounts, profiles, and authentication | Supports FR1-FR5 | NFR5 |
| Notification Service | Sends confirmations, reminders, and alerts to users | Supports FR1-FR5 | NFR2 |
| Analytics Service | Collects and processes usage data for business intelligence | Supports system optimization | NFR1, NFR4 |
CheckCapacity: Check parking lot capacityコピー
GET /api/v1/lots/{lot_id}/capacity
Parameters:
- vehicle_type: Type of vehicle (car, motorcycle, etc.), string, required
- start_date_time: ISO8601 format, required
- end_date_time: ISO8601 format, required
Returns:
- available_spots: Number of available spots, integer
- price: Estimated price for the duration, decimal
- status_codes: 200 OK, 400 Bad Request, 404 Lot Not Found
ReserveSpot: Create a parking spot reservationコピー
POST /api/v1/reservations
Body:
- user_id: User identifier, string, required
- lot_id: Parking lot identifier, string, required
- vehicle_type: Type of vehicle, string, required
- start_date_time: ISO8601 format, required
- end_date_time: ISO8601 format, required
Returns:
- reservation_id: Unique reservation identifier, string
- price: Total price for the reservation, decimal
- payment_url: URL to complete payment, string
- status_codes: 201 Created, 400 Bad Request, 409 Conflict (if lot is full)
CompleteReservation: Finalize reservation after paymentコピー
POST /api/v1/reservations/{reservation_id}/complete
Body:
- user_id: User identifier, string, required
- payment_token: Payment confirmation token, string, required
Returns:
- status: Reservation status, string
- confirmation_code: Code for entry to lot, string
- status_codes: 200 OK, 400 Bad Request, 404 Not Found, 402 Payment Required
VehicleArrived: Record vehicle check-inコピー
POST /api/v1/transactions/arrival
Body:
- reservation_id: Reservation identifier, string, required
- date_time: Arrival timestamp, ISO8601 format, required
Returns:
- transaction_id: Unique transaction identifier, string
- status: Updated reservation status, string
- status_codes: 200 OK, 404 Not Found
VehicleLeft: Record vehicle check-outコピー
POST /api/v1/transactions/departure
Body:
- reservation_id: Reservation identifier, string, required
- date_time: Departure timestamp, ISO8601 format, required
Returns:
- transaction_id: Unique transaction identifier, string
- status: Updated reservation status, string
- final_charge: Final amount charged (if different from reservation), decimal
- status_codes: 200 OK, 404 Not Found
GetLotOccupancy: Get current occupancy for a lotコピー
GET /api/v1/admin/lots/{lot_id}/occupancy
Parameters:
- date: Optional date for historical data, ISO8601 format
Returns:
- capacity: Total lot capacity, integer
- occupied_spots: Number of occupied spots, integer
- reservations: List of active reservations
- status_codes: 200 OK, 403 Forbidden, 404 Not Found
erDiagram
User ||--o{ Reservation : makes
User ||--o{ Vehicle : owns
Reservation ||--|| Spot : reserves
Reservation ||--o{ Transaction : generates
Reservation ||--o{ Payment : has
Spot }|--|| ParkingLot : belongs_to
ParkingLot ||--o{ ContactInfo : has
User ||--o{ ContactInfo : has
User {
string user_id PK
string name
string email
string phone
datetime created_at
}
Vehicle {
string vehicle_id PK
string user_id FK
string license_plate
string vehicle_type
datetime created_at
}
Reservation {
string reservation_id PK
string user_id FK
string spot_id FK
string vehicle_id FK
datetime start_time
datetime end_time
string payment_status
string completion_status
datetime created_at
}
Spot {
string spot_id PK
string lot_id FK
string spot_type
boolean is_active
}
ParkingLot {
string lot_id PK
string name
string address
int capacity
string lot_type
boolean is_active
}
Transaction {
string transaction_id PK
string reservation_id FK
datetime check_in_time
datetime check_out_time
decimal final_charge
}
Payment {
string payment_id PK
string reservation_id FK
decimal amount
string payment_method
string payment_status
datetime payment_time
}
ContactInfo {
string contact_id PK
string entity_id FK
string contact_type
string contact_value
}
We'll use a relational database (PostgreSQL) for primary storage due to:
Data will be partitioned by parking lot ID, which ensures:
flowchart TD
Client[Client Applications]
GatewayLB[API Gateway Load Balancer]
APIGateway[API Gateway]
ResLB[Reservation Service LB]
RS[Reservation Service]
PayLB[Payment Service LB]
PS[Payment Service]
TxLB[Transaction Service LB]
TS[Transaction Service]
TMS[Transaction Monitor Service]
NS[Notification Service]
US[User Service]
AS[Analytics Service]
Redis[(Redis Cache)]
RDBM[(Primary DB)]
RDBS[(Read Replicas)]
CDN[CDN]
ThirdParty[Third-Party Payment]
Client --> GatewayLB
GatewayLB --> APIGateway
Client --> CDN
APIGateway --> ResLB
APIGateway --> PayLB
APIGateway --> TxLB
APIGateway --> US
ResLB --> RS
PayLB --> PS
TxLB --> TS
RS --> Redis
RS --> RDBM
PS --> RDBM
PS --> ThirdParty
TS --> RDBM
TS --> Redis
US --> RDBM
TMS --> RDBM
TMS --> NS
RDBM --> RDBS
AS --> RDBS
NS --> Client
subgraph "Service Layer"
RS
PS
TS
US
TMS
NS
AS
end
subgraph "Data Layer"
Redis
RDBM
RDBS
end
style Client fill:#f9f,stroke:#333,stroke-width:2px
style APIGateway fill:#bbf,stroke:#33f,stroke-width:2px
style Redis fill:#bfb,stroke:#3f3,stroke-width:2px
style RDBM fill:#fbb,stroke:#f33,stroke-width:2px
style RDBS fill:#ffb,stroke:#ff3,stroke-width:2px
sequenceDiagram
actor User
participant App as Mobile/Web App
participant API as API Gateway
participant RS as Reservation Service
participant Cache as Redis Cache
participant DB as Database
participant PS as Payment Service
participant PP as Payment Provider
participant NS as Notification Service
User->>App: 1. Check parking availability
App->>API: 2. GET /lots/{id}/capacity
API->>RS: 3. Forward request
RS->>Cache: 4. Check capacity in cache
alt Capacity in cache
Cache-->>RS: 5a. Return cached data
else Cache miss
RS->>DB: 5b. Query database
DB-->>RS: 6b. Return capacity data
RS->>Cache: 7b. Update cache
end
RS-->>API: 8. Return availability & price
API-->>App: 9. Return response
App-->>User: 10. Display available spots
User->>App: 11. Reserve spot
App->>API: 12. POST /reservations
API->>RS: 13. Forward request
RS->>DB: 14. Create reservation (uncommitted)
alt Successful reservation
DB-->>RS: 15a. Reservation created
RS->>PS: 16a. Request payment
PS->>PP: 17a. Forward to payment provider
PP-->>PS: 18a. Return payment URL/token
PS-->>RS: 19a. Return payment info
RS-->>API: 20a. Return reservation & payment URL
API-->>App: 21a. Return response
App-->>User: 22a. Redirect to payment
else Reservation failed (e.g., spot taken)
DB-->>RS: 15b. Error (e.g., conflict)
RS-->>API: 16b. Return error
API-->>App: 17b. Return error response
App-->>User: 18b. Show error & alternatives
end
User->>PP: 23. Complete payment
PP->>PS: 24. Payment callback
PS->>DB: 25. Update payment status
PS->>RS: 26. Notify payment complete
RS->>DB: 27. Commit reservation
RS->>NS: 28. Send confirmation
NS-->>User: 29. Email/SMS confirmation
sequenceDiagram
actor Attendant
participant Terminal as Parking Terminal
participant API as API Gateway
participant TS as Transaction Service
participant DB as Database
participant RS as Reservation Service
participant NS as Notification Service
actor Customer
Customer->>Terminal: 1. Present reservation code
Terminal->>API: 2. Verify reservation
API->>RS: 3. Validate reservation
RS->>DB: 4. Check reservation status
DB-->>RS: 5. Return reservation details
alt Valid reservation
RS-->>API: 6a. Reservation confirmed
API-->>Terminal: 7a. Allow entry
Terminal-->>Attendant: 8a. Open gate
Attendant->>Terminal: 9a. Confirm vehicle entered
Terminal->>API: 10a. POST /transactions/arrival
API->>TS: 11a. Record arrival
TS->>DB: 12a. Create transaction record
TS->>DB: 13a. Update reservation status
else Invalid reservation
RS-->>API: 6b. Reservation invalid
API-->>Terminal: 7b. Deny entry
Terminal-->>Attendant: 8b. Keep gate closed
end
Note over Customer, Terminal: Some time later...
Customer->>Terminal: 14. Present ticket at exit
Terminal->>API: 15. Get reservation details
API->>TS: 16. Request checkout
TS->>DB: 17. Retrieve reservation & transaction
DB-->>TS: 18. Return details
alt Early exit (before reservation end)
TS->>DB: 19a. Update transaction record
TS-->>API: 20a. Return exit approved
API-->>Terminal: 21a. Allow exit
Terminal-->>Attendant: 22a. Open gate
else Late exit (additional charges)
TS->>RS: 19b. Calculate additional charges
RS->>TS: 20b. Return updated charges
TS->>DB: 21b. Update transaction with charges
TS-->>API: 22b. Return exit approval with charges
API-->>Terminal: 23b. Display additional charges
Terminal-->>Customer: 24b. Request payment
Customer->>Terminal: 25b. Make payment
Terminal->>API: 26b. Process additional payment
API-->>Terminal: 27b. Payment confirmed
Terminal-->>Attendant: 28b. Open gate
end
TS->>NS: 29. Send receipt
NS-->>Customer: 30. Email receipt
The Reservation Service is a critical component requiring special attention due to its impact on consistency and user satisfaction. The key challenge is efficiently finding available spots while preventing double-booking.
Each parking spot's availability is tracked using a bitmap:
コピー
function findAvailableSpot(lotId, startTime, endTime, vehicleType):
// Convert times to slot indices
startSlot = convertTimeToSlotIndex(startTime)
endSlot = convertTimeToSlotIndex(endTime)
// Create a bitmap representing requested time
requestBitmap = createBitmapForTimeRange(startSlot, endSlot)
// Get spots with compatible vehicle type
eligibleSpots = getEligibleSpots(lotId, vehicleType)
// Find closest available spot (transaction with SELECT FOR UPDATE)
BEGIN TRANSACTION
for spot in eligibleSpots:
// Get bitmap for relevant days
spotBitmaps = getSpotBitmapsForDays(spot.id, startTime, endTime)
// Check if any timeslot overlaps
if !hasOverlap(spotBitmaps, requestBitmap):
// Update spot's bitmap with the new reservation
updateSpotBitmaps(spot.id, requestBitmap, startTime, endTime)
COMMIT TRANSACTION
return spot
ROLLBACK TRANSACTION
return null // No available spot found
The strength of this approach:
Memory requirement for the bitmap approach:
Limitations:
This service handles edge cases such as no-shows, late arrivals, and early departures.
コピー
function monitorNoShows():
// Find reservations that have started but have no check-in record
cutoffTime = currentTime - NO_SHOW_BUFFER
// Query for potential no-shows
possibleNoShows = queryReservations(
where: "start_time < cutoffTime AND completion_status = 'to_be_completed'
AND NOT EXISTS (select 1 from transactions where transactions.reservation_id = reservations.id)"
)
foreach reservation in possibleNoShows:
// Apply no-show policy
markAsNoShow(reservation.id)
applyCharges(reservation.id, NO_SHOW_CHARGE_DURATION)
// Release the spot for future reservations
updateSpotAvailability(reservation.spot_id, reservation.start_time, reservation.end_time)
// Notify user
sendNoShowNotification(reservation.user_id)
This approach ensures:
| OptionProsConsDecision | |||
| Relational DB (PostgreSQL) | - Strong consistency guarantees<br>- ACID transactions<br>- Complex query support<br>- Well-suited for relational data | - Horizontal scaling challenges<br>- Potentially higher latency for writes | Selected for core transactional data due to consistency requirements |
| NoSQL (e.g., MongoDB) | - Horizontal scaling<br>- Flexible schema<br>- Potentially higher write throughput | - Eventual consistency<br>- Less support for complex joins<br>- Transaction limitations | Not selected for core data, but could be used for analytics |
| OptionProsConsDecision | |||
| In-Memory (Single Node Redis) | - Lower latency<br>- Simpler setup<br>- No data partitioning | - Limited by single node capacity<br>- Single point of failure | Used for development and small deployments |
| Distributed Cache (Redis Cluster) | - Horizontal scalability<br>- Higher availability<br>- Partitioned data | - Higher operational complexity<br>- Network overhead | Selected for production to support scaling requirements |
| OptionProsConsDecision | |||
| Pessimistic Locking | - Prevents conflicts entirely<br>- Simpler error handling | - Reduced concurrency<br>- Potential for deadlocks | Used for critical operations like final reservation confirmation |
| Optimistic Locking | - Higher concurrency<br>- Better performance under low contention | - Conflicts require retry logic<br>- More complex error handling | Selected for initial reservation creation with timeout mechanism |