Files
Laca-City/Documents/SYSTEM_ARCHITECTURE.md
PhongPham e0e47d57c7 feat: Enhanced CSS animations, improved UI components, and project reorganization
- 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
2025-08-03 07:00:22 +07:00

17 KiB

🏗️ Smart Parking Finder - System Architecture Documentation

📋 Table of Contents

  1. System Overview
  2. Architecture Patterns
  3. Technology Stack
  4. Data Structure & Schema
  5. System Design
  6. API Documentation
  7. Database Design
  8. 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

{
  "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

{
  "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

{
  "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

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

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

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

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

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

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

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

// 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

// 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

@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

// 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

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

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

POST /api/routing/calculate
GET /api/routing/modes
POST /api/routing/optimize

Health & Monitoring

GET /api/health
GET /api/health/database
GET /api/health/cache
GET /api/metrics

🗄️ Database Design

Indexing Strategy

-- 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

-- 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

// 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