Skip to content

An over-engineered weekend project exploring gRPC streaming, the LGTM observability stack, and geospatial queries on Kubernetes via a real-time EV charging simulator.

Notifications You must be signed in to change notification settings

frodi-karlsson/ev-sim

Repository files navigation

EV Simulator

A microservices-based Electric Vehicle simulation platform running on Kubernetes.

Note: A weekend project built to practice gRPC streaming, the LGTM Stack (Loki, Grafana, Prometheus), Kubernetes, and PostGIS. Intentionally over-engineered.


UI Demo

Functionality

Frontend (Go HTTP)

  • Visualization: Renders a graph of devices surrounding a user-selected coordinate.
  • Management: Allows adding, editing, and connecting devices.
  • Streaming: Bridges gRPC streams to SSE for live charge/status updates.

Simulation Engine (Go)

Background worker managing the charging logic:

  • Cut-off: Charging stops automatically at 100%.
  • Drain: Idle devices lose 1% battery per tick.
  • Load Balancing: Devices under 20% attempt to charge. Access is denied if the local grid cell (1km radius) has > 5 active chargers.

Architecture

flowchart TB
    User((Browser)) 
    
    subgraph K8s [Kubernetes Cluster]
        direction TB
        
        subgraph Ingress [Service Layer]
            Web[HTTP Service<br/>Go]
        end

        subgraph Backend [Private Network]
            NodeAPI[Core API<br/>Node]
            Engine[<b>Sim Engine</b><br/>Go]
            Migrator([Migration Job<br/>Ephemeral])
            DB[(Postgres<br/>PostGIS)]
        end

        subgraph Obs [Observability Namespace]
            direction TB
            Grafana
            Loki
            Prometheus
            Promtail([Promtail<br/>DaemonSet])
        end
    end

    %% Real-time User Flow (SSE + HTTP)
    User <-->|HTTP/SSE| Web
    Web <-->|"ConnectRPC<br/>(Unary + Stream)"| NodeAPI
    
    %% Operational Access
    User -.->|Port Forward<br/>localhost:3000| Grafana

    %% Database Access
    NodeAPI -->|Read/Write| DB
    Engine -->|Tick/Update| DB
    Migrator -->|Schema Updates| DB
    
    %% Metrics Pipeline (Prometheus Scrapes Services)
    Prometheus -->|Scrape Metrics| Web & NodeAPI & Engine

    %% Logging Pipeline (Promtail tails containers)
    Web & NodeAPI & Engine -.->|Stdout| Promtail
    Promtail -->|Push Logs| Loki
    
    %% Visualization
    Grafana -->|LogQL| Loki
    Grafana -->|PromQL| Prometheus

    %% Styling
    classDef go fill:#00add8,stroke:#333,color:white;
    classDef node fill:#68a063,stroke:#333,color:white;
    classDef db fill:#336791,stroke:#333,color:white;
    classDef obs fill:#e6522c,stroke:#333,color:white;
    classDef sim fill:#f96,stroke:#333,color:white;
    
    class Web go;
    class NodeAPI node;
    class DB,Loki,Prometheus db;
    class Promtail,Grafana obs;
    class Engine sim;
Loading

Domains & Services

1. HTTP Service (/http)

  • Language: Go 1.25
  • Role: Backend-for-Frontend (BFF) & Charting.
  • Security: No DB access. Communicates via ConnectRPC.

2. Core API Service (/rest)

  • Language: Node.js (>=24) / TypeScript
  • Role: Domain logic and persistence.
  • Transport: ConnectRPC (Unary + Server Streaming).
  • Database: Postgres 16 + PostGIS.

3. Simulation Engine (/engine)

  • Language: Go 1.25
  • Role: Simulation ticker.
  • Logic: Handles battery decay and ST_SnapToGrid spatial capacity checks.
  • Testing: Integration tests via Testcontainers.

Streaming Data Flow

  1. Engine writes new charge state to DB.
  2. Browser opens EventSource to Go HTTP.
  3. Go HTTP opens gRPC Stream to Node API.
  4. Node API streams DB updates to Go.
  5. Go HTTP pushes updates to Browser via SSE.

Observability (LGTM Stack)

Uses the loki-stack Helm chart to deploy a self-hosted telemetry suite.

Observability Dashboard

Management via Makefile.monitoring:

Target Description
make -f Makefile.monitoring deploy Install grafana/loki-stack (Loki, Prom, Grafana)
make -f Makefile.monitoring port-forward Access Grafana (localhost:3000)
make -f Makefile.monitoring password Retrieve Grafana admin password
make -f Makefile.monitoring delete Uninstall Stack

Development Workflow

1. Build

make build-rest
make build-http
make build-engine

2. Deploy

Applies Kustomize overlay, migration jobs, and network policies.

make deploy-dev

3. Access

# Go Web UI (http://localhost:30080)
make port-forward-http

4. Logs

# Stream all logs
make logs ENV=dev

# Stream specific service
kubectl logs -f -l app=engine -n ev-sim-dev

5. Protobuf & ConnectRPC

Source: /proto/device.proto.

# Regenerate code
cd http && make proto
cd rest && pnpm proto

Makefiles

Most useful operations are standardized across the project via Makefiles.

  • Root Makefile: Handles high-level K8s operations (deploy, delete, status).
  • Module Makefiles: Each service (/http, /engine, /rest) contains a dedicated Makefile for component-level tasks like make deps, make test, or make help.

Root Makefile Reference:

Target Description
deploy-dev Apply Kustomize overlay
delete-dev Delete resources
restart-dev Rollout restart
status Show Pod/Service status

About

An over-engineered weekend project exploring gRPC streaming, the LGTM observability stack, and geospatial queries on Kubernetes via a real-time EV charging simulator.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published