A modern, scalable vehicle tracking system built with Event Sourcing and CQRS patterns, powered by PostgreSQL and TypeScript. This project demonstrates the powerful advantages of event sourcing for real-world applications requiring audit trails, temporal queries, and bulletproof data consistency.
This system implements a clean Event Sourcing architecture with Command Query Responsibility Segregation (CQRS):
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ HTTP API │ │ Event Store │ │ Projections │
│ (Commands) │───โถ│ (PostgreSQL) │───โถ│ (Read Models) │
└─────────────────┘ └──────────────────┘ └─────────────────────┘
│
โผ
┌──────────────────┐
│ Event Bus │
│ (Subscribers) │
└──────────────────┘
- Commands:
RegisterVehicleCommand,TrackLocationCommand - Queries:
GetVehicle,VehicleCurrentLocationQuery - Events:
VehicleRegistered,LocationTracked - Projections:
VehicleCurrentLocation(materialized view) - Event Store: PostgreSQL-based immutable event log
Every state change is captured as an immutable event, providing:
- Full history of all vehicle registrations and location updates
- Compliance-ready audit logs for regulatory requirements
- Forensic analysis capabilities for debugging and investigation
// Every location update creates an immutable event
{
eventType: "LocationTracked",
payload: {
locationTrackedId: "uuid-v7",
latitude: 40.7128,
longitude: -74.0060,
scope: { vehicleRegisteredId: "vehicle-uuid" }
},
timestamp: "2024-01-15T10:30:00Z"
}Reconstruct system state at any point in time:
- Historical reporting: "Where was vehicle ABC-123 at 2PM yesterday?"
- Debugging: "What was the system state when the issue occurred?"
- Analytics: Track movement patterns over time
- Immutable events prevent data corruption
- Append-only storage eliminates race conditions
- Event ordering ensures consistent state reconstruction
- CQRS separation allows independent scaling of reads and writes
- Multiple projections from the same event stream
- Optimized queries with materialized views
- ACID compliance for reliable event storage
- JSON support for flexible event payloads
- Mature ecosystem with excellent tooling
- Cost-effective compared to specialized event stores
- Bun runtime
- Docker & Docker Compose
- PostgreSQL 17
- Clone and install dependencies:
git clone <repository-url>
cd es_vehicle_tracking
bun install- Start PostgreSQL:
docker-compose up -d- Run the application:
bun startThe API will be available at http://localhost:3000
POST /register-vehicle
Content-Type: application/json
{
"vehicleLicensePlate": "ABC-1234"
}Response:
{
"vehicleRegisteredId": "0199456d-fdb5-7000-a29f-b78450c75fd9"
}POST /track-location
Content-Type: application/json
{
"vehicleLicensePlate": "ABC-1234",
"latitude": 40.7128,
"longitude": -74.0060
}Response:
{
"locationTrackedId": "0199456e-2db3-7000-b45f-c89d60e82a1b"
}GET /vehicle-current-location/{vehicleRegisteredId}Response:
{
"vehicleRegisteredId": "0199456d-fdb5-7000-a29f-b78450c75fd9",
"vehicleLicensePlate": "ABC-1234",
"latitude": 40.7128,
"longitude": -74.0060
}- Command received via HTTP API
- Business logic validates the command
- Event generated and stored in event store
- Projections updated asynchronously via event listeners
- Read models provide optimized query performance
Event 1: VehicleRegistered
├─ vehicleRegisteredId: "uuid-1"
├─ vehicleLicensePlate: "ABC-1234"
└─ registeredAt: "2024-01-15T09:00:00Z"
Event 2: LocationTracked
├─ locationTrackedId: "uuid-2"
├─ latitude: 40.7128
├─ longitude: -74.0060
└─ scope: { vehicleRegisteredId: "uuid-1" }
Event 3: LocationTracked
├─ locationTrackedId: "uuid-3"
├─ latitude: 40.7589
├─ longitude: -73.9851
└─ scope: { vehicleRegisteredId: "uuid-1" }
Built on @ricofritzsche/eventstore with PostgreSQL backend:
- Immutable event log with automatic versioning
- Optimistic concurrency control prevents conflicts
- Event filtering and querying capabilities
Real-time materialized views updated via event listeners:
// Vehicle current location projection
export async function handleLocationTracked(event: EventRecord) {
await pg`
UPDATE vehicle_current_location
SET latitude = ${latitude}, longitude = ${longitude}
WHERE vehicle_registered_id = ${vehicleRegisteredId}
`;
}- Commands: Modify state by appending events
- Queries: Read from optimized projections
- Separation: Independent scaling and optimization
Recreate any projection from the event stream:
export async function rebuildVehicleCurrentLocationProjection(
eventStore: EventStore,
connectionString: string
) {
// Truncate existing projection
await pg`TRUNCATE TABLE vehicle_current_location`;
// Replay all events
const { events } = await eventStore.query(createQuery(
createFilter(["VehicleRegistered", "LocationTracked"])
));
// Rebuild state from events
for (const event of events) {
await handleEvent(event);
}
}- Graceful degradation when projections lag
- Event replay for failed projections
- Idempotent event handlers prevent duplicate processing
- Append-only writes are extremely fast
- No complex joins during writes
- Minimal locking in PostgreSQL
- Optimized projections for specific query patterns
- Denormalized data eliminates complex joins
- Caching-friendly materialized views
- Horizontal scaling of read replicas
- Event stream partitioning for high-throughput scenarios
- Microservice decomposition via bounded contexts
- Immutable audit trail for compliance reporting
- Data retention policies via event archiving
- Change tracking for regulatory investigations
- Historical analysis of vehicle movement patterns
- Temporal queries for business intelligence
- Event-driven analytics pipelines
- Complete system history for troubleshooting
- Event replay for reproducing issues
- Detailed logging of all state changes
Use the included api.http file with your favorite HTTP client (VS Code REST Client, Postman, etc.):
### Register a vehicle
POST http://localhost:3000/register-vehicle
Content-Type: application/json
{
"vehicleLicensePlate": "TEST-001"
}
### Track location
POST http://localhost:3000/track-location
Content-Type: application/json
{
"vehicleLicensePlate": "TEST-001",
"latitude": 37.7749,
"longitude": -122.4194
}- Runtime: Bun - Fast JavaScript runtime
- Framework: Hono - Lightweight web framework
- Event Store: @ricofritzsche/eventstore - PostgreSQL event store
- Database: PostgreSQL 17 - Reliable, ACID-compliant storage
- Validation: Zod - TypeScript-first schema validation
- Language: TypeScript - Type-safe development
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Submit a pull request
Built with โค๏ธ and Event Sourcing
This project demonstrates the power of event sourcing for building scalable, auditable, and maintainable systems. The combination of PostgreSQL's reliability with modern TypeScript tooling creates a robust foundation for real-world applications.
This readme was generated using an LLM