Files
usda-vision/docs/MODULARIZATION_PROPOSAL.md
salirezav f6a37ca1ba Remove deprecated files and scripts to streamline the codebase
- Deleted unused API test files, RTSP diagnostic scripts, and development utility scripts to reduce clutter.
- Removed outdated database schema and modularization proposal documents to maintain focus on current architecture.
- Cleaned up configuration files and logging scripts that are no longer in use, enhancing project maintainability.
2025-11-02 10:07:59 -05:00

12 KiB

Camera Management API - Modularization Proposal

📊 Current Architecture Analysis

Current Structure

The camera-management-api is currently a monolithic service with the following components:

  1. API Server (api/server.py)

    • FastAPI REST endpoints
    • WebSocket real-time updates
    • Orchestrates all other components
  2. Camera Management (camera/)

    • Camera discovery & initialization
    • Recording (CameraRecorder)
    • Streaming (CameraStreamer) - MJPEG & RTSP
    • Camera monitoring & recovery
  3. MQTT Client (mqtt/)

    • Machine state monitoring
    • Event publishing
  4. Storage Manager (storage/)

    • File indexing
    • Storage statistics
    • Cleanup operations
  5. Auto Recording Manager (recording/)

    • Automated recording based on MQTT events
    • Standalone auto-recorder
  6. Core Services

    • State Manager (in-memory state)
    • Event System (pub/sub)
    • Configuration management
  7. Video Services (video/)

    • Video streaming
    • Metadata extraction
    • Caching

Current Service Separation

You already have:

  • media-api - Video processing (thumbnails, transcoding)
  • mediamtx - RTSP streaming server
  • Microfrontend dashboard (shell + video-remote)

🎯 Modularization Strategies

Approach: Keep as single service but improve internal structure with clear boundaries.

Structure:

camera-management-api/
├── core/              # Shared infrastructure
│   ├── state/
│   ├── events/
│   └── config/
├── camera/            # Camera hardware layer
├── recording/         # Recording logic
├── streaming/         # Streaming logic (separate from camera)
├── mqtt/              # MQTT integration
├── storage/           # Storage operations
└── api/               # API endpoints (orchestration layer)

Pros:

  • Minimal disruption to working system
  • Easier debugging (single process)
  • Lower operational complexity
  • Shared state remains simple
  • No network latency between components
  • Easier to maintain consistency

Cons:

  • Can't scale components independently
  • All-or-nothing deployment
  • Single point of failure (mitigated by Docker)

Best For: Current state - proven system that works well together


Strategy 2: Strategic Microservices (Hybrid Approach)

Approach: Split only high-value, independently scalable components.

Services:

Service 1: camera-service (Critical, Hardware-Dependent)

Responsibilities:
- Camera discovery & initialization
- Recording (CameraRecorder)
- Streaming (CameraStreamer) - MJPEG
- Camera monitoring & recovery
- Hardware state management

Port: 8001
Dependencies: Camera SDK, FFmpeg
Network: host (for camera access)

Service 2: mqtt-service (Stateless, Scalable)

Responsibilities:
- MQTT client & subscriptions
- Machine state monitoring
- Event publishing

Port: 8002
Dependencies: MQTT broker
Stateless: Yes

Service 3: api-gateway (Orchestration)

Responsibilities:
- REST API endpoints
- WebSocket server
- Request routing to services
- Aggregating responses

Port: 8000
Dependencies: camera-service, mqtt-service, state-manager

Service 4: state-manager-service (Optional - Shared State)

Responsibilities:
- Centralized state management
- Event bus/queue
- State persistence

Port: 8003
Database: Redis (recommended) or PostgreSQL

Pros:

  • Camera service can be isolated/restarted independently
  • MQTT service is stateless and scalable
  • Clear separation of concerns
  • Can scale MQTT service separately
  • API gateway can handle load balancing

Cons:

  • More complex deployment
  • Network latency between services
  • State synchronization challenges
  • More containers to manage
  • Service discovery needed

Best For: Future scaling needs, when you need:

  • Multiple camera servers
  • High MQTT message volume
  • Different scaling requirements per component

Strategy 3: Full Microservices (Advanced)

Approach: Split into granular services following domain boundaries.

Services:

  1. camera-service - Hardware control
  2. recording-service - Recording orchestration
  3. streaming-service - MJPEG/RTSP streaming
  4. mqtt-service - Machine state monitoring
  5. auto-recording-service - Automated recording logic
  6. api-gateway - API & routing
  7. state-service - Centralized state
  8. storage-service - File management

Pros:

  • Maximum flexibility
  • Independent scaling per service
  • Technology diversity (can use different languages)
  • Team autonomy

Cons:

  • Very complex
  • High operational overhead
  • Distributed system challenges (consistency, latency)
  • Overkill for current needs

Best For: Large team, complex requirements, need for maximum flexibility


Phase 1: Internal Refactoring (Current → 3 months)

Goal: Improve code organization without breaking changes

  1. Separate concerns within monolith:

    camera/
    ├── hardware/        # Camera SDK operations
    ├── recording/       # Recording logic
    ├── streaming/       # Streaming logic
    └── monitoring/      # Health checks
    
  2. Use dependency injection: Pass dependencies explicitly

  3. Clear interfaces: Define contracts between modules

  4. Document boundaries: Mark what can/can't be changed independently

Outcome: Cleaner code, easier to split later if needed


Phase 2: Extract MQTT Service (3-6 months)

Goal: Split out stateless, independent component

Why MQTT first?

  • Completely stateless
  • No shared state with cameras
  • Easy to scale
  • Lower risk (doesn't affect camera operations)

Implementation:

  • Move mqtt/ to separate service
  • Use Redis/RabbitMQ for event pub/sub
  • API Gateway queries MQTT service for status
  • MQTT service publishes to event bus

Outcome: First microservice, validates approach


Phase 3: Evaluate Further Splitting (6+ months)

Decision Point: Based on actual needs

If scaling cameras:

  • Extract camera-service to run on multiple machines
  • Keep recording/streaming together (they're tightly coupled)

If high API load:

  • Keep API gateway separate
  • Scale gateway independently

If complex state management:

  • Extract state-service with Redis/PostgreSQL
  • Services query state service instead of in-memory

🔧 Implementation Details

Shared Infrastructure (All Strategies)

1. Event Bus (Essential for microservices)

Option A: Redis Pub/Sub (lightweight)
Option B: RabbitMQ (more features)
Option C: MQTT (you already have it!)

2. State Management

Option A: Redis (fast, in-memory)
Option B: PostgreSQL (persistent, queryable)
Option C: Keep in-memory for now (simplest)

3. Service Discovery

For microservices:
- Docker Compose service names (simple)
- Consul/Eureka (if needed)
- Kubernetes services (if migrating)

4. API Gateway Pattern

nginx/Envoy: Route requests to services
FastAPI Gateway: Aggregate responses
GraphQL: Alternative aggregation layer

📋 Decision Matrix

Factor Modular Monolith Strategic Split Full Microservices
Complexity Low Medium High
Scalability Limited Good Excellent
Development Speed Fast Medium Slow
Operational Overhead Low Medium High
Risk Low Medium High
Cost Low Medium High
Current Fit Perfect Good Overkill

💡 My Recommendation

Start with Strategy 1: Modular Monolith + Internal Refactoring

Why?

  1. Your system is already working well
  2. RTSP + Recording work concurrently (hard problem solved)
  3. No immediate scaling needs identified
  4. Single team managing it
  5. Lower risk, faster improvements

What to do now:

  1. Refactor internal structure (Phase 1)

    • Separate camera, recording, streaming modules
    • Clear interfaces between modules
    • Dependency injection
  2. Add event bus infrastructure (prepare for future)

    • Set up Redis for events (even if monolith)
    • Publish events through Redis pub/sub
    • Services can subscribe when needed
  3. Monitor & Measure (data-driven decisions)

    • Track performance metrics
    • Identify bottlenecks
    • Measure actual scaling needs
  4. Extract when needed (not before)

    • Only split when you have concrete problems
    • Start with MQTT service (safest first split)
    • Then camera-service if scaling cameras

Red Flags for Microservices (when you DON'T need them):

  • "We might need to scale" (YAGNI - You Ain't Gonna Need It)
  • "Industry best practice" (without actual need)
  • "Multiple teams" (you have one team)
  • "Independent deployment" (current deployment is simple)

Green Flags for Microservices (when you DO need them):

  • Actually scaling cameras to multiple servers
  • High API load requiring independent scaling
  • Need to update camera logic without touching MQTT
  • Multiple teams working on different components
  • Need different technology stacks per service

🚀 Quick Start: Internal Refactoring Plan

Step 1: Create Module Boundaries

usda_vision_system/
├── camera/
│   ├── hardware/          # Camera SDK wrapper
│   │   ├── camera_sdk.py
│   │   └── device_discovery.py
│   ├── recording/         # Recording logic
│   │   ├── recorder.py
│   │   └── video_writer.py
│   ├── streaming/         # Streaming logic
│   │   ├── mjpeg_streamer.py
│   │   └── rtsp_streamer.py
│   └── monitoring/        # Health & recovery
│       └── health_check.py

Step 2: Define Interfaces

# camera/domain/interfaces.py
class ICameraHardware(ABC):
    @abstractmethod
    def initialize() -> bool
    @abstractmethod
    def capture_frame() -> Frame

class IRecorder(ABC):
    @abstractmethod
    def start_recording(filename: str) -> bool
    @abstractmethod
    def stop_recording() -> bool

Step 3: Dependency Injection

# Instead of direct instantiation
recorder = CameraRecorder(config, state_manager, event_system)

# Use factories/interfaces
recorder = RecorderFactory.create(config, dependencies)

📚 References & Further Reading


Questions to Answer

Before deciding on microservices, ask:

  1. Do you need to scale components independently?

    • If no → Monolith is fine
  2. Do different teams work on different parts?

    • If no → Monolith is fine
  3. Are there actual performance bottlenecks?

    • If no → Don't optimize prematurely
  4. Can you deploy the monolith easily?

    • If yes → Monolith might be better
  5. Do you need different tech stacks per component?

    • If no → Monolith is fine

Bottom Line: Your system is working well. Focus on improving code quality and organization rather than splitting prematurely. Extract services when you have concrete, measurable problems that require it.