- Enhanced globals.css with comprehensive animation system - Added advanced map marker animations (GPS, parking) - Improved button and filter animations with hover effects - Added new UI components: BookingModal, ParkingDetails, WheelPicker - Reorganized project structure with better documentation - Added optimization scripts and improved development workflow - Updated deployment guides and technical documentation - Enhanced mobile responsiveness and accessibility support
612 lines
17 KiB
Markdown
612 lines
17 KiB
Markdown
# 🏗️ Smart Parking Finder - System Architecture Documentation
|
|
|
|
## 📋 Table of Contents
|
|
1. [System Overview](#system-overview)
|
|
2. [Architecture Patterns](#architecture-patterns)
|
|
3. [Technology Stack](#technology-stack)
|
|
4. [Data Structure & Schema](#data-structure--schema)
|
|
5. [System Design](#system-design)
|
|
6. [API Documentation](#api-documentation)
|
|
7. [Database Design](#database-design)
|
|
8. [Security & Performance](#security--performance)
|
|
|
|
---
|
|
|
|
## 🎯 System Overview
|
|
|
|
### Project Description
|
|
Smart Parking Finder is a real-time parking management system that helps users find available parking spots in urban areas. The system provides interactive mapping, route optimization, and real-time availability tracking.
|
|
|
|
### Core Features
|
|
- 🗺️ Interactive map with OpenStreetMap integration
|
|
- 🔍 Real-time parking spot search
|
|
- 🧭 GPS-based navigation and routing
|
|
- 📱 Responsive web application
|
|
- 🔄 Real-time data synchronization
|
|
- 📊 Analytics and reporting
|
|
|
|
### System Goals
|
|
- **Performance**: Sub-second response times for searches
|
|
- **Scalability**: Support 10,000+ concurrent users
|
|
- **Reliability**: 99.9% uptime availability
|
|
- **Usability**: Intuitive interface for all user types
|
|
|
|
---
|
|
|
|
## 🏛️ Architecture Patterns
|
|
|
|
### 1. Microservices Architecture
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Client Layer │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ Next.js Frontend (React 18 + TypeScript + Tailwind CSS) │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────┴─────────┐
|
|
│ API Gateway │
|
|
│ (NestJS Router) │
|
|
└─────────┬─────────┘
|
|
│
|
|
┌─────────────────────┼─────────────────────┐
|
|
│ │ │
|
|
┌───────▼───────┐ ┌─────────▼─────────┐ ┌───────▼───────┐
|
|
│ Auth Service │ │ Parking Service │ │Routing Service│
|
|
│ (JWT) │ │ (CRUD + Search) │ │ (Valhalla) │
|
|
└───────────────┘ └───────────────────┘ └───────────────┘
|
|
│ │ │
|
|
└─────────────────────┼─────────────────────┘
|
|
│
|
|
┌─────────▼─────────┐
|
|
│ Data Layer │
|
|
│ PostgreSQL + Redis│
|
|
└───────────────────┘
|
|
```
|
|
|
|
### 2. Layered Architecture
|
|
- **Presentation Layer**: Next.js React Components
|
|
- **Business Logic Layer**: NestJS Services & Controllers
|
|
- **Data Access Layer**: TypeORM Repositories
|
|
- **Database Layer**: PostgreSQL with Redis Cache
|
|
|
|
### 3. Event-Driven Architecture
|
|
- Real-time updates using WebSocket connections
|
|
- Event sourcing for parking availability changes
|
|
- Message queuing for background tasks
|
|
|
|
---
|
|
|
|
## 🛠️ Technology Stack
|
|
|
|
### Frontend Stack
|
|
```json
|
|
{
|
|
"framework": "Next.js 14",
|
|
"runtime": "React 18",
|
|
"language": "TypeScript 5.x",
|
|
"styling": "Tailwind CSS 3.x",
|
|
"mapping": "React Leaflet + OpenStreetMap",
|
|
"state": "React Query (TanStack Query)",
|
|
"forms": "React Hook Form",
|
|
"testing": "Jest + React Testing Library"
|
|
}
|
|
```
|
|
|
|
### Backend Stack
|
|
```json
|
|
{
|
|
"framework": "NestJS 10",
|
|
"runtime": "Node.js 18+",
|
|
"language": "TypeScript 5.x",
|
|
"database": "PostgreSQL 15",
|
|
"cache": "Redis 7",
|
|
"orm": "TypeORM",
|
|
"authentication": "JWT + Passport",
|
|
"validation": "Class Validator",
|
|
"documentation": "Swagger/OpenAPI",
|
|
"testing": "Jest + Supertest"
|
|
}
|
|
```
|
|
|
|
### Infrastructure Stack
|
|
```json
|
|
{
|
|
"containerization": "Docker + Docker Compose",
|
|
"routing": "Valhalla Routing Engine",
|
|
"mapping": "OpenStreetMap Data",
|
|
"monitoring": "Health Check Endpoints",
|
|
"logging": "Winston Logger",
|
|
"process": "PM2 Process Manager"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Data Structure & Schema
|
|
|
|
### 1. Database Schema
|
|
|
|
#### Users Table
|
|
```sql
|
|
CREATE TABLE users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
full_name VARCHAR(255) NOT NULL,
|
|
phone VARCHAR(20),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
is_active BOOLEAN DEFAULT true,
|
|
role user_role DEFAULT 'customer'
|
|
);
|
|
|
|
CREATE TYPE user_role AS ENUM ('customer', 'operator', 'admin');
|
|
```
|
|
|
|
#### Parking Lots Table
|
|
```sql
|
|
CREATE TABLE parking_lots (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(255) NOT NULL,
|
|
address TEXT NOT NULL,
|
|
latitude DECIMAL(10,8) NOT NULL,
|
|
longitude DECIMAL(11,8) NOT NULL,
|
|
total_spots INTEGER NOT NULL DEFAULT 0,
|
|
available_spots INTEGER NOT NULL DEFAULT 0,
|
|
price_per_hour DECIMAL(10,2) NOT NULL,
|
|
operating_hours JSONB,
|
|
amenities TEXT[],
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
is_active BOOLEAN DEFAULT true
|
|
);
|
|
|
|
-- Spatial index for location-based queries
|
|
CREATE INDEX idx_parking_lots_location ON parking_lots USING GIST (
|
|
ll_to_earth(latitude, longitude)
|
|
);
|
|
```
|
|
|
|
#### Parking Spots Table
|
|
```sql
|
|
CREATE TABLE parking_spots (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
parking_lot_id UUID REFERENCES parking_lots(id) ON DELETE CASCADE,
|
|
spot_number VARCHAR(20) NOT NULL,
|
|
spot_type spot_type DEFAULT 'regular',
|
|
is_occupied BOOLEAN DEFAULT false,
|
|
is_reserved BOOLEAN DEFAULT false,
|
|
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(parking_lot_id, spot_number)
|
|
);
|
|
|
|
CREATE TYPE spot_type AS ENUM ('regular', 'disabled', 'electric', 'compact');
|
|
```
|
|
|
|
#### Reservations Table
|
|
```sql
|
|
CREATE TABLE reservations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
parking_spot_id UUID REFERENCES parking_spots(id) ON DELETE CASCADE,
|
|
start_time TIMESTAMP NOT NULL,
|
|
end_time TIMESTAMP NOT NULL,
|
|
total_cost DECIMAL(10,2) NOT NULL,
|
|
status reservation_status DEFAULT 'pending',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TYPE reservation_status AS ENUM ('pending', 'confirmed', 'active', 'completed', 'cancelled');
|
|
```
|
|
|
|
### 2. API Data Models
|
|
|
|
#### Parking Lot Response Model
|
|
```typescript
|
|
interface ParkingLot {
|
|
id: string;
|
|
name: string;
|
|
address: string;
|
|
coordinates: {
|
|
latitude: number;
|
|
longitude: number;
|
|
};
|
|
availability: {
|
|
totalSpots: number;
|
|
availableSpots: number;
|
|
occupancyRate: number;
|
|
};
|
|
pricing: {
|
|
hourlyRate: number;
|
|
currency: string;
|
|
discounts?: Discount[];
|
|
};
|
|
operatingHours: {
|
|
[day: string]: {
|
|
open: string;
|
|
close: string;
|
|
is24Hours: boolean;
|
|
};
|
|
};
|
|
amenities: string[];
|
|
distance?: number;
|
|
estimatedWalkTime?: number;
|
|
metadata: {
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
isActive: boolean;
|
|
};
|
|
}
|
|
```
|
|
|
|
#### Search Request Model
|
|
```typescript
|
|
interface ParkingSearchRequest {
|
|
location: {
|
|
latitude: number;
|
|
longitude: number;
|
|
};
|
|
radius: number; // in meters
|
|
filters?: {
|
|
maxPrice?: number;
|
|
amenities?: string[];
|
|
spotTypes?: SpotType[];
|
|
availableOnly?: boolean;
|
|
};
|
|
sorting?: {
|
|
field: 'distance' | 'price' | 'availability';
|
|
order: 'asc' | 'desc';
|
|
};
|
|
pagination?: {
|
|
page: number;
|
|
limit: number;
|
|
};
|
|
}
|
|
```
|
|
|
|
#### Route Response Model
|
|
```typescript
|
|
interface RouteResponse {
|
|
route: {
|
|
distance: number; // in meters
|
|
duration: number; // in seconds
|
|
coordinates: [number, number][]; // [lng, lat] pairs
|
|
};
|
|
instructions: RouteInstruction[];
|
|
summary: {
|
|
totalDistance: string;
|
|
totalTime: string;
|
|
estimatedCost: number;
|
|
};
|
|
}
|
|
|
|
interface RouteInstruction {
|
|
text: string;
|
|
distance: number;
|
|
time: number;
|
|
sign: number;
|
|
interval: [number, number];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 System Design
|
|
|
|
### 1. Frontend Architecture
|
|
|
|
#### Component Hierarchy
|
|
```
|
|
App (layout.tsx)
|
|
├── Header
|
|
├── LocationDetector
|
|
├── MapView
|
|
│ ├── LeafletMap
|
|
│ ├── ParkingMarkers
|
|
│ ├── RouteLayer
|
|
│ └── LocationMarker
|
|
├── ParkingList
|
|
│ ├── ParkingCard
|
|
│ └── PaginationControls
|
|
├── BookingModal
|
|
├── TransportationSelector
|
|
└── GPSSimulator (dev only)
|
|
```
|
|
|
|
#### State Management
|
|
```typescript
|
|
// Global State Structure
|
|
interface AppState {
|
|
user: {
|
|
profile: UserProfile | null;
|
|
authentication: AuthState;
|
|
preferences: UserPreferences;
|
|
};
|
|
location: {
|
|
current: Coordinates | null;
|
|
permissions: LocationPermission;
|
|
tracking: boolean;
|
|
};
|
|
parking: {
|
|
searchResults: ParkingLot[];
|
|
selectedLot: ParkingLot | null;
|
|
filters: SearchFilters;
|
|
loading: boolean;
|
|
error: string | null;
|
|
};
|
|
routing: {
|
|
currentRoute: RouteResponse | null;
|
|
isCalculating: boolean;
|
|
transportMode: TransportMode;
|
|
};
|
|
ui: {
|
|
mapCenter: Coordinates;
|
|
mapZoom: number;
|
|
sidebarOpen: boolean;
|
|
modalState: ModalState;
|
|
};
|
|
}
|
|
```
|
|
|
|
### 2. Backend Architecture
|
|
|
|
#### Service Layer Design
|
|
```typescript
|
|
// Parking Service Architecture
|
|
@Injectable()
|
|
export class ParkingService {
|
|
constructor(
|
|
private readonly parkingRepository: ParkingRepository,
|
|
private readonly cacheService: CacheService,
|
|
private readonly geoService: GeoService
|
|
) {}
|
|
|
|
async findNearbyParking(searchDto: ParkingSearchDto): Promise<ParkingLot[]> {
|
|
// 1. Check cache first
|
|
const cacheKey = this.generateCacheKey(searchDto);
|
|
const cached = await this.cacheService.get(cacheKey);
|
|
if (cached) return cached;
|
|
|
|
// 2. Perform spatial query
|
|
const results = await this.parkingRepository.findWithinRadius({
|
|
latitude: searchDto.latitude,
|
|
longitude: searchDto.longitude,
|
|
radius: searchDto.radius
|
|
});
|
|
|
|
// 3. Apply filters and sorting
|
|
const filtered = this.applyFilters(results, searchDto.filters);
|
|
const sorted = this.applySorting(filtered, searchDto.sorting);
|
|
|
|
// 4. Cache results
|
|
await this.cacheService.set(cacheKey, sorted, 300); // 5 min cache
|
|
|
|
return sorted;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Repository Pattern
|
|
```typescript
|
|
@EntityRepository(ParkingLot)
|
|
export class ParkingRepository extends Repository<ParkingLot> {
|
|
async findWithinRadius(params: SpatialQueryParams): Promise<ParkingLot[]> {
|
|
return this.createQueryBuilder('parking')
|
|
.select()
|
|
.addSelect(`
|
|
(6371 * acos(
|
|
cos(radians(:lat)) * cos(radians(latitude)) *
|
|
cos(radians(longitude) - radians(:lng)) +
|
|
sin(radians(:lat)) * sin(radians(latitude))
|
|
)) AS distance
|
|
`)
|
|
.where(`
|
|
(6371 * acos(
|
|
cos(radians(:lat)) * cos(radians(latitude)) *
|
|
cos(radians(longitude) - radians(:lng)) +
|
|
sin(radians(:lat)) * sin(radians(latitude))
|
|
)) <= :radius
|
|
`)
|
|
.andWhere('is_active = true')
|
|
.setParameters({
|
|
lat: params.latitude,
|
|
lng: params.longitude,
|
|
radius: params.radius / 1000 // Convert to km
|
|
})
|
|
.orderBy('distance', 'ASC')
|
|
.getMany();
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Real-time Updates
|
|
|
|
#### WebSocket Integration
|
|
```typescript
|
|
// WebSocket Gateway for real-time updates
|
|
@WebSocketGateway({
|
|
cors: { origin: '*' },
|
|
transports: ['websocket', 'polling']
|
|
})
|
|
export class ParkingGateway implements OnGatewayConnection, OnGatewayDisconnect {
|
|
@WebSocketServer() server: Server;
|
|
|
|
async handleConnection(client: Socket) {
|
|
// Subscribe client to location-based updates
|
|
const { latitude, longitude, radius } = client.handshake.query;
|
|
const room = this.generateLocationRoom(latitude, longitude, radius);
|
|
client.join(room);
|
|
}
|
|
|
|
@SubscribeMessage('parkingUpdate')
|
|
async handleParkingUpdate(client: Socket, data: ParkingUpdateDto) {
|
|
// Broadcast to relevant clients
|
|
const affectedRooms = this.getAffectedRooms(data.location);
|
|
affectedRooms.forEach(room => {
|
|
this.server.to(room).emit('parkingAvailabilityChanged', {
|
|
parkingLotId: data.parkingLotId,
|
|
availableSpots: data.availableSpots,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔗 API Documentation
|
|
|
|
### Authentication Endpoints
|
|
```typescript
|
|
POST /api/auth/login
|
|
POST /api/auth/register
|
|
POST /api/auth/refresh
|
|
DELETE /api/auth/logout
|
|
GET /api/auth/profile
|
|
PUT /api/auth/profile
|
|
```
|
|
|
|
### Parking Endpoints
|
|
```typescript
|
|
GET /api/parking/search
|
|
GET /api/parking/:id
|
|
POST /api/parking/:id/reserve
|
|
GET /api/parking/reservations
|
|
PUT /api/parking/reservations/:id
|
|
DELETE /api/parking/reservations/:id
|
|
```
|
|
|
|
### Routing Endpoints
|
|
```typescript
|
|
POST /api/routing/calculate
|
|
GET /api/routing/modes
|
|
POST /api/routing/optimize
|
|
```
|
|
|
|
### Health & Monitoring
|
|
```typescript
|
|
GET /api/health
|
|
GET /api/health/database
|
|
GET /api/health/cache
|
|
GET /api/metrics
|
|
```
|
|
|
|
---
|
|
|
|
## 🗄️ Database Design
|
|
|
|
### Indexing Strategy
|
|
```sql
|
|
-- Spatial indexes for location queries
|
|
CREATE INDEX idx_parking_lots_location ON parking_lots
|
|
USING GIST (ll_to_earth(latitude, longitude));
|
|
|
|
-- Compound indexes for filtering
|
|
CREATE INDEX idx_parking_lots_active_price ON parking_lots (is_active, price_per_hour);
|
|
CREATE INDEX idx_parking_spots_lot_available ON parking_spots (parking_lot_id, is_occupied);
|
|
|
|
-- Time-based indexes for reservations
|
|
CREATE INDEX idx_reservations_time_range ON reservations (start_time, end_time);
|
|
CREATE INDEX idx_reservations_user_status ON reservations (user_id, status, created_at);
|
|
```
|
|
|
|
### Data Partitioning
|
|
```sql
|
|
-- Partition reservations by month for better performance
|
|
CREATE TABLE reservations_2024_01 PARTITION OF reservations
|
|
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
|
|
|
|
-- Automated partition management
|
|
CREATE OR REPLACE FUNCTION create_monthly_partition()
|
|
RETURNS void AS $$
|
|
DECLARE
|
|
start_date date;
|
|
end_date date;
|
|
table_name text;
|
|
BEGIN
|
|
start_date := date_trunc('month', CURRENT_DATE + interval '1 month');
|
|
end_date := start_date + interval '1 month';
|
|
table_name := 'reservations_' || to_char(start_date, 'YYYY_MM');
|
|
|
|
EXECUTE format('CREATE TABLE %I PARTITION OF reservations
|
|
FOR VALUES FROM (%L) TO (%L)',
|
|
table_name, start_date, end_date);
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
## 🔒 Security & Performance
|
|
|
|
### Security Measures
|
|
1. **Authentication**: JWT with refresh tokens
|
|
2. **Authorization**: Role-based access control (RBAC)
|
|
3. **Input Validation**: DTO validation with class-validator
|
|
4. **SQL Injection**: Protected by TypeORM parameterized queries
|
|
5. **Rate Limiting**: API rate limiting per user/IP
|
|
6. **CORS**: Configured for specific origins
|
|
7. **HTTPS**: TLS encryption in production
|
|
|
|
### Performance Optimizations
|
|
1. **Caching**: Redis for frequently accessed data
|
|
2. **Database**: Optimized indexes and query planning
|
|
3. **CDN**: Static asset delivery optimization
|
|
4. **Compression**: Gzip compression for API responses
|
|
5. **Lazy Loading**: Component and route-based code splitting
|
|
6. **Pagination**: Efficient pagination for large datasets
|
|
7. **Connection Pooling**: Database connection optimization
|
|
|
|
### Monitoring & Logging
|
|
```typescript
|
|
// Health Check Implementation
|
|
@Controller('health')
|
|
export class HealthController {
|
|
constructor(
|
|
private readonly healthCheckService: HealthCheckService,
|
|
private readonly databaseHealthIndicator: TypeOrmHealthIndicator,
|
|
private readonly redisHealthIndicator: RedisHealthIndicator
|
|
) {}
|
|
|
|
@Get()
|
|
@HealthCheck()
|
|
check() {
|
|
return this.healthCheckService.check([
|
|
() => this.databaseHealthIndicator.pingCheck('database'),
|
|
() => this.redisHealthIndicator.checkHealth('redis'),
|
|
() => this.checkExternalServices()
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📈 Scalability Considerations
|
|
|
|
### Horizontal Scaling
|
|
- **Load Balancing**: Multiple application instances
|
|
- **Database Sharding**: Geographic or user-based sharding
|
|
- **Microservices**: Independent service scaling
|
|
- **CDN Integration**: Global content distribution
|
|
|
|
### Vertical Scaling
|
|
- **Database Optimization**: Query optimization and indexing
|
|
- **Memory Management**: Efficient caching strategies
|
|
- **CPU Optimization**: Algorithmic improvements
|
|
- **Storage Optimization**: Data archiving and compression
|
|
|
|
### Future Enhancements
|
|
- **Machine Learning**: Predictive parking availability
|
|
- **Mobile Apps**: Native iOS/Android applications
|
|
- **Payment Integration**: Online payment processing
|
|
- **IoT Integration**: Smart parking sensor integration
|
|
- **Multi-language**: Internationalization support
|
|
|
|
---
|
|
|
|
*Last Updated: August 3, 2025*
|
|
*Version: 1.0.0*
|