Production-grade REST API for managing cargo shipments, vessels, and tracking history with clean architecture and event-driven patterns.
DEUS Logistics API is a production-grade backend service for managing maritime cargo operations. Built with Go 1.21+ and following clean architecture principles with domain-driven design, the API handles cargo shipment lifecycle management, vessel tracking, and immutable transaction history. The system features event-driven architecture with Kafka integration, structured logging, comprehensive error handling, and production-grade operational practices including health checks and testcontainers integration tests.
| Feature | Description |
|---|---|
| π¦ Cargo Management | Create, retrieve, list, and update cargo shipment status with validated state transitions (pending β in_transit β delivered) |
| π’ Vessel Management | Create, retrieve, list, and update vessel information including location tracking |
| π Append-Only Tracking | Immutable tracking entries per cargo with database-level constraints preventing updates or deletions |
| π¨ Kafka Event-Driven | Publish cargo status changes to Kafka topics with in-process consumer goroutine for event persistence |
| π Structured Logging | JSON-formatted logs with request tracing via context propagation for observability |
| π Centralized Error Mapping | Consistent error responses with HTTP status codes, error codes, and request IDs |
| β€οΈ Health Checks | Liveness probe (/health) and readiness probe (/ready) with dependency validation |
| π§ͺ Integration Testing | Testcontainers support for real PostgreSQL and Kafka integration tests |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HTTP Client β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β REST
βββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β Transport Layer (Gin) β
β Handlers Β· DTOs Β· Error Mapping β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β Application Layer β
β Use Cases Β· Orchestration Β· Validation β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β Domain Layer β
β Entities Β· State Machine Β· Business Rules β
β cargo/ Β· vessel/ Β· tracking/ (per-entity pkg) β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β Infrastructure Layer β
β PostgreSQL (pgx) Β· Kafka Producer Β· Consumer β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Layer | Package | Responsibility |
|---|---|---|
| Transport | internal/transport/http/ |
Gin handlers, DTOs, error mapping, request/response serialization |
| Application | internal/application/cargo/ |
Use cases, orchestration, validation, business workflows |
| Domain | internal/domain/cargo/, vessel/, tracking/ |
Entities, state machine, business rules, repository interfaces |
| Infrastructure | internal/postgres/, internal/events/ |
PostgreSQL (pgx), Kafka producer/consumer, migrations |
| Service | internal/service/ |
Vessel and tracking service implementations |
PATCH /api/v1/cargoes/:id/status
β
βΌ
UpdateCargoStatusUseCase.Execute()
β
ββββ DB: UPDATE cargoes SET status = ?
β
ββββ DB: INSERT INTO tracking_entries (append-only)
β
ββββ Kafka: publish β "cargo-status-changes" topic
β
βΌ
Consumer goroutine (same process)
β
βΌ
DB: INSERT INTO cargo_events (append-only)
| Category | Technology | Version/Notes |
|---|---|---|
| Language | Go | 1.21+ |
| HTTP Framework | Gin Web Framework | Latest |
| Database | PostgreSQL | 15-alpine |
| DB Driver | pgx with connection pooling | Latest |
| Migrations | golang-migrate | Latest |
| Event Bus | Apache Kafka | 7.5.0 |
| Kafka Client | segmentio/kafka-go | Latest |
| Logging | Zerolog | Latest |
| Testing | testify + testcontainers | Latest |
| Containers | Docker + Docker Compose | Docker Desktop 3.6+ / Engine 23+ |
deus-logistics-api/
βββ cmd/
β βββ api/
β βββ main.go # Single binary entry point (API + consumer)
βββ internal/
β βββ application/
β β βββ cargo/
β β βββ manager.go # Dependency injection wiring
β β βββ use_cases.go # Create, get, list, update status
β β βββ interfaces.go # Repository interfaces
β βββ domain/
β β βββ cargo/
β β β βββ models.go # Cargo, CargoStatus
β β β βββ errors.go # Domain-specific errors
β β β βββ events.go # StatusChangedEvent
β β β βββ repository.go # Repository interface
β β βββ vessel/
β β β βββ models.go # Vessel, VesselStatus
β β β βββ errors.go # ErrNotFound, ErrCapacityExceeded
β β β βββ repository.go # Repository interface
β β βββ tracking/
β β βββ models.go # TrackingEntry, AddTrackingInput
β β βββ errors.go # Validation errors
β β βββ repository.go # Append-only interface
β βββ service/
β β βββ cargo_service.go # Cargo service implementation
β β βββ vessel_service.go # Vessel service implementation
β β βββ tracking_service.go # Tracking service implementation
β βββ postgres/
β β βββ db.go # Connection pool setup
β β βββ migrations/
β β β βββ 000001_init.up.sql # Create tables
β β β βββ 000001_init.down.sql # Rollback
β β βββ queries/
β β β βββ cargo.sql # sqlc cargo queries
β β β βββ vessel.sql # sqlc vessel queries
β β β βββ tracking.sql # sqlc tracking queries
β β βββ cargo_repo.go # sqlc implementation
β β βββ vessel_repo.go # sqlc implementation
β β βββ tracking_repo.go # sqlc implementation (append-only)
β βββ events/
β β βββ producer.go # Kafka producer
β β βββ consumer.go # Kafka consumer (goroutine)
β βββ transport/
β β βββ http/
β β βββ router.go # Route registration
β β βββ cargo_handler.go # Cargo endpoints
β β βββ vessel_handler.go # Vessel endpoints
β β βββ tracking_handler.go # Tracking endpoints
β β βββ health_handler.go # Health check endpoints
β β βββ error_handler.go # Error mapping
β βββ health/
β β βββ reporter.go # Health check logic
β βββ validation/
β β βββ validator.go # Input validation rules
β βββ errors/
β β βββ errors.go # Global error types
β βββ config/
β βββ config.go # Environment configuration
βββ pkg/
β βββ response/
β βββ constants.go # Error codes, response envelopes
β βββ error.go # Response wrapper
βββ Dockerfile # Multi-stage build: golang:1.21-alpine β alpine:3.18
βββ docker-compose.yml # PostgreSQL, Kafka, Zookeeper, API
βββ Makefile # Build targets, tests, migrations
βββ .env.example # Configuration template
βββ .gitignore # Git ignore rules
βββ README.md # This file
| Requirement | Version | Purpose |
|---|---|---|
| Go | 1.21+ | Build and run locally |
| Docker | 24+ | Run containerized services |
| Docker Compose | 2.x | Service orchestration |
| make | any | Build automation |
git clone https://github.com/alex-necsoiu/deus-logistics-api.git
cd deus-logistics-apicp .env.example .env
# Edit .env if needed (defaults work with docker-compose)make docker-runThis starts:
- PostgreSQL 15 on
localhost:5432 - Kafka on
localhost:9092(internal:localhost:29092) - Zookeeper on
localhost:2181 - API on
localhost:8080
# Liveness check
curl http://localhost:8080/health
# Readiness check (verifies DB connectivity)
curl http://localhost:8080/readyExpected output:
{
"status": "healthy",
"check": {
"name": "liveness",
"status": "healthy"
}
}Once the API is running, access the interactive Swagger UI documentation:
π http://localhost:8080/swagger/index.html
The Swagger UI enables you to:
- Browse all endpoints with full documentation
- View request/response schemas with examples
- Test endpoints directly from the browser
- Download API specification (JSON/YAML)
Swagger documentation is generated from Go code annotations using Swaggo. To regenerate after modifying handlers or DTOs:
# Using Make (recommended)
make swagger
# Or directly
swag init -g cmd/api/main.goThis updates:
docs/docs.go- Embedded documentation (auto-imported in main.go)docs/swagger.json- OpenAPI 3.0 specification (JSON)docs/swagger.yaml- OpenAPI 3.0 specification (YAML)
All endpoints are documented with:
- Summary - Short operation description
- Parameters - Request payload with validation rules
- Responses - Success responses with example data
- Errors - Possible error codes and messages
- Examples - Realistic request/response examples
For detailed annotation reference, see SWAGGER.md.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/cargoes |
Create a cargo shipment |
| GET | /api/v1/cargoes |
List all cargoes |
| GET | /api/v1/cargoes/:id |
Get cargo by ID |
| PATCH | /api/v1/cargoes/:id/status |
Update cargo status |
| POST | /api/v1/vessels |
Create a vessel |
| GET | /api/v1/vessels |
List all vessels |
| GET | /api/v1/vessels/:id |
Get vessel by ID |
| PATCH | /api/v1/vessels/:id/location |
Update vessel location |
| POST | /api/v1/cargoes/:id/tracking |
Add tracking entry |
| GET | /api/v1/cargoes/:id/tracking |
Get tracking history |
| GET | /health |
Liveness check |
| GET | /ready |
Readiness check |
curl -X POST http://localhost:8080/api/v1/cargoes \
-H "Content-Type: application/json" \
-d '{
"name": "Container ABC-123",
"description": "Electronics shipment",
"weight": 500.0,
"vessel_id": "550e8400-e29b-41d4-a716-446655440000"
}'Response (201 Created):
{
"data": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"name": "Container ABC-123",
"description": "Electronics shipment",
"weight": 500,
"status": "pending",
"vessel_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
},
"meta": {
"request_id": "a3f1b2c4-e29b-41d4-a716-446655440000"
}
}Valid transitions: pending β in_transit β delivered
curl -X PATCH http://localhost:8080/api/v1/cargoes/7c9e6679-7425-40de-944b-e07fc1f90ae7/status \
-H "Content-Type: application/json" \
-d '{
"status": "in_transit"
}'Response (200 OK):
{
"data": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"name": "Container ABC-123",
"status": "in_transit",
"updated_at": "2025-01-15T10:35:00Z"
},
"meta": {
"request_id": "a3f1b2c4-e29b-41d4-a716-446655440000"
}
}Invalid transition (422 Unprocessable Entity):
curl -X PATCH http://localhost:8080/api/v1/cargoes/7c9e6679-7425-40de-944b-e07fc1f90ae7/status \
-H "Content-Type: application/json" \
-d '{
"status": "delivered"
}'Response (422 Unprocessable Entity):
{
"error": {
"code": "INVALID_TRANSITION",
"message": "Cannot transition from in_transit to delivered only from in_transit",
"request_id": "a3f1b2c4-e29b-41d4-a716-446655440000"
}
}curl http://localhost:8080/api/v1/cargoes/7c9e6679-7425-40de-944b-e07fc1f90ae7/trackingResponse (200 OK):
{
"data": [
{
"id": "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p",
"cargo_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"location": "Shanghai Port",
"status": "pending",
"note": "Cargo received at origin port",
"created_at": "2025-01-15T10:30:00Z"
},
{
"id": "2b3c4d5e-6f7g-8h9i-0j1k-2l3m4n5o6p7q",
"cargo_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"location": "En route to Rotterdam",
"status": "in_transit",
"note": "Vessel departed Shanghai",
"created_at": "2025-01-15T10:35:00Z"
}
],
"meta": {
"request_id": "a3f1b2c4-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "The requested resource was not found.",
"request_id": "a3f1b2c4-e29b-41d4-a716-446655440000"
}
}| HTTP | Code | Meaning |
|---|---|---|
| 400 | INVALID_INPUT |
Request validation failed (missing/invalid fields) |
| 404 | NOT_FOUND |
Resource does not exist |
| 422 | INVALID_TRANSITION |
State machine rejected the transition |
| 500 | INTERNAL_ERROR |
Unexpected server error |
vessels
βββ cargoes (vessel_id β vessels.id ON DELETE RESTRICT)
βββ tracking_entries (cargo_id β cargoes.id APPEND-ONLY π)
βββ cargo_events (cargo_id β cargoes.id APPEND-ONLY π)
- Cargo Status Constraint:
CHECK (status IN ('pending', 'in_transit', 'delivered')) - Tracking Append-Only: Database-level trigger prevents UPDATE/DELETE on
tracking_entries - Events Append-Only: Database-level trigger prevents UPDATE/DELETE on
cargo_events - Foreign Keys: Referential integrity with
ON DELETE RESTRICTfor data consistency
| File | Purpose |
|---|---|
000001_init.up.sql |
Create vessels, cargoes, tracking_entries, cargo_events tables |
000001_init.down.sql |
Drop all tables in reverse dependency order |
000002_enforce_append_only_tracking.up.sql |
Add triggers for append-only enforcement |
000003_enhance_schema_integrity.up.sql |
Add additional indexes and constraints |
| Variable | Default | Description |
|---|---|---|
DB_HOST |
postgres |
PostgreSQL hostname |
DB_PORT |
5432 |
PostgreSQL port |
DB_USER |
postgres |
Database user |
DB_PASSWORD |
postgres |
Database password |
DB_NAME |
deus_logistics_db |
Database name |
DB_SSL_MODE |
disable |
SSL mode: disable or require |
SERVER_PORT |
8080 |
API listen port |
SERVER_ENV |
development |
Environment: development or production |
KAFKA_BROKER |
kafka:29092 |
Kafka broker address (internal for docker) |
KAFKA_TOPIC_STATUS_CHANGES |
cargo-status-changes |
Kafka topic for status change events |
LOG_LEVEL |
info |
Log level: debug, info, warn, error |
| Command | Description |
|---|---|
make test |
Run all tests (unit + integration) |
make test-unit |
Unit tests only (no Docker required) |
make test-integration |
Integration tests with testcontainers |
make test-race |
Run all tests with race detector |
make test-coverage |
Generate HTML coverage report (fails if < 80%) |
| Package | Coverage | Status |
|---|---|---|
internal/domain/cargo |
~100% | β Complete |
internal/domain/vessel |
~100% | β Complete |
internal/domain/tracking |
~100% | β Complete |
internal/application/cargo |
~70% | |
internal/service |
~65% | |
internal/transport/http |
~60% | |
internal/postgres |
~40% |
Integration tests use testcontainers-go to spin up real PostgreSQL and Kafka instances:
# Requires Docker to be running
go test ./internal/postgres/... -v -run TestCargoRepository
# With race detector
make test-race| Target | Description |
|---|---|
make help |
Display all available targets |
make install-tools |
Install sqlc, golang-migrate, mockgen |
make build |
Compile API binary (with version embedding) |
make run |
Build and run locally |
make generate |
Run go generate (mockgen) |
make fmt |
Format all Go files |
make fmt-check |
Check formatting (CI-safe, exits 1 if issues) |
make vet |
Run static analysis (go vet) |
make lint |
Run golangci-lint |
make sqlc |
Generate type-safe code from SQL queries |
make test |
Run all tests |
make test-unit |
Unit tests only |
make test-race |
Tests with race detector |
make test-coverage |
Coverage report (enforces 80% minimum) |
make docker-build |
Build Docker image |
make docker-run / make dev-up |
Start full Docker stack |
make docker-down / make dev-down |
Stop all containers |
make docker-logs / make logs |
Tail container logs |
make docker-ps / make ps |
Show container status |
make migrate-up |
Run pending migrations |
make migrate-down |
Rollback last migration |
make migrate-create |
Create new migration pair |
make clean |
Remove build artifacts |
All logs are emitted as JSON for machine parsing:
{
"level": "info",
"time": "2025-01-15T10:30:45Z",
"caller": "application/cargo/update_status.go:95",
"cargo_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"old_status": "pending",
"new_status": "in_transit",
"message": "business_event:cargo_status_updated"
}Generate mocks and sqlc code:
make generate # Runs go generate ./...
make sqlc # Regenerate sqlc from SQL queries| Service | Image | Port | Purpose |
|---|---|---|---|
api |
Built from Dockerfile |
8080 |
DEUS Logistics API |
postgres |
postgres:15-alpine |
5432 |
Primary database |
kafka |
confluentinc/cp-kafka:7.5.0 |
9092, 29092 |
Event streaming bus |
zookeeper |
confluentinc/cp-zookeeper:7.5.0 |
2181 |
Kafka coordination |
# Stage 1: Build (golang:1.21-alpine)
# - Install dependencies
# - Compile with ldflags (version, build time, commit)
# - Result: ~18MB binary
# Stage 2: Runtime (alpine:3.18)
# - Copy binary only
# - Health checks
# - Final image: ~20MBAll services include health checks:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3MIT License β Copyright (c) 2026 Alex Necsoiu
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software.
Alex Necsoiu
- GitHub: @alex-necsoiu
- LinkedIn: alex-necsoiu
- Email: axel.necsoiu@gmail.com
π‘ For questions or contributions, please open an issue or submit a pull request on GitHub.