Skip to content

System Overview

Viana is a single Spring Boot monolith. All modules — auth, rides, location, ratings, vehicles, documents — live in the same process. The external dependencies are PostgreSQL (persistent data), Redis (ephemeral location data), and S3 (document file storage).


High-level diagram

graph TB
    subgraph Clients
        C[Customer App]
        D[Driver App]
        A[Admin]
    end

    subgraph Viana Monolith [:8080]
        AUTH[Auth Module<br/>JWT · Refresh Tokens]
        RIDES[Rides Module<br/>Matching · Lifecycle]
        LOC[Location Module<br/>Geo · Fares]
        VEH[Vehicles Module<br/>Vehicles · Documents]
        RATE[Ratings Module]
        WS[WebSocket<br/>STOMP / SockJS]
        ASYNC[Async Executor<br/>10–50 threads]
    end

    subgraph Data
        PG[(PostgreSQL + PostGIS<br/>:5432)]
        RD[(Redis Geo<br/>:6379)]
        S3[(AWS S3<br/>Documents)]
    end

    C -->|REST /api| AUTH
    D -->|REST /api| AUTH
    A -->|REST /api/admin| AUTH
    C -->|STOMP /ws| WS
    D -->|STOMP /ws| WS

    AUTH --> PG
    RIDES --> PG
    RIDES --> ASYNC
    LOC --> PG
    LOC --> RD
    VEH --> PG
    VEH --> S3
    RATE --> PG
    WS --> RIDES
    WS --> LOC

Module breakdown

Module Package Responsibility
auth modules/auth User registration, login, JWT generation, token lifecycle, refresh
users modules/users Driver and Customer profiles
rides modules/rides Ride creation, matching, lifecycle state machine
location modules/location Driver location updates (Redis + PostGIS), fare estimation, nearby search
vehicles modules/vehicles Vehicle CRUD, document upload and admin review
ratings modules/ratings Post-ride ratings for drivers and customers
notifications modules/notifications WebSocket push to drivers (ride offers) and customers (location, status)
configuration configuration Security, WebSocket, Redis, Async, S3, OpenAPI

Request flow

Every inbound HTTP request passes through:

Request → CorsFilter → JwtAuthenticationFilter (validates Bearer token, sets SecurityContext) → UsernamePasswordAuthenticationFilter → DispatcherServlet → @PreAuthorize role check → Controller → Service → Repository → Response

Every WebSocket handshake passes through:

STOMP CONNECT → JwtHandshakeInterceptor (reads Authorization: Bearer header, validates JWT) → JwtHandshakeHandler (sets Principal = userId) → MessageBroker


Technology decisions

Why a monolith? At this stage, a monolith is the right choice. Ride matching, location updates, and WebSocket notifications all need to share in-process state (the async timer map, the messaging template). A service mesh would add latency and coordination overhead with no benefit at this scale.

Why Redis for location? Driver positions update every few seconds. PostgreSQL with PostGIS is not designed for that write frequency. Redis Geo commands (GEOADD, GEORADIUS) are O(N+log M) on writes and reads, and the entire dataset fits in RAM. PostGIS is used as the fallback path for persistent spatial queries.

Why Reactor Mono for the acceptance timer? The 30-second acceptance window must not block a thread. Thread.sleep inside @Async would waste a thread for each open ride. Mono.delay schedules the cancellation on Reactor's scheduler without holding a JVM thread.