Shorty Pro is a distributed URL shortener built as a small event-driven system. It splits the write and redirect workload from analytics processing so the main redirect path stays fast while click events are captured asynchronously.
The project consists of two Spring Boot services and a supporting local infrastructure stack:
url-shortener-servicecreates short codes, resolves original URLs, serves redirects, and manages Redis-backed caching.analytics-serviceconsumes click events from Kafka and stores them in a separate analytics database.- PostgreSQL is used for persistent storage.
- Redis is used to accelerate read-heavy redirect and lookup operations.
- Kafka transports click events from the redirect flow to the analytics pipeline.
- Prometheus, Grafana, Loki, Alloy, Kafka UI, and RedisInsight provide local observability and operations tooling.
At a high level, the flow is:
- A client creates a short URL through
url-shortener-service. - The mapping is stored in PostgreSQL and can be read back directly or served through Redis cache.
- A redirect request hits
url-shortener-service. - The service returns
302 Foundand publishes a click event to Kafka. analytics-serviceconsumes the event and persists it into the analytics PostgreSQL database.
Supported operations:
- Create short URLs.
- Resolve a short code back to its original URL.
- Redirect a short code to its destination URL.
- Inspect cache status.
- Clear all caches or a specific cache.
- Trigger asynchronous cache warmup.
Implementation details:
- Built with Spring Boot 3.5 and Java 21.
- Uses Spring Data JDBC with Flyway migrations against PostgreSQL.
- Uses Redis through Spring Cache for hot-path reads.
- Publishes click events to Kafka.
- Exposes Actuator health and Prometheus metrics endpoints.
- Exposes Swagger/OpenAPI UI for interactive API inspection.
Supported operations:
- Consume URL click events from Kafka.
- Persist click events into a dedicated analytics database.
- Ignore malformed or duplicate events safely.
Implementation details:
- Event-driven service; there is currently no business REST controller for analytics reads.
- Uses Spring Kafka consumers with explicit topic and DLT configuration.
- Uses PostgreSQL plus Flyway migrations for event persistence.
- Treats duplicate deliveries as safe by relying on a unique
event_id. - Exposes Actuator health and Prometheus metrics endpoints.
- Also exposes Swagger/OpenAPI infrastructure, although the business API surface is currently event-based.
The repository includes two local execution modes:
- Docker Compose for a full local stack with databases, messaging, applications, and observability.
- Kubernetes manifests under
k8n/README.mdfor Minikube-based local deployment.
- Java 21
- Spring Boot 3.5
- Spring Web
- Spring Validation
- Spring Data JDBC
- Spring Data Redis
- Spring Cache
- Spring Kafka
- PostgreSQL 15
- Redis 7
- Apache Kafka with ZooKeeper
- Flyway
- Micrometer + Prometheus registry
- Grafana
- Loki
- Alloy
- Docker / Docker Compose
- Kubernetes / Minikube
- Gradle
- Java 21
- Docker and Docker Compose
- Make
From the repository root:
make buildUseful commands:
make start
make logs
make status
make downService ports:
url-shortener-service:8082analytics-service:8081kafka-ui:8080grafana:3000prometheus:9090redisinsight:5540url-shortener-postgres:5434analytics-postgres:5435redis:6379kafka:29092
Kubernetes manifests are already prepared under k8n.
Start here:
That guide covers:
- starting Minikube
- building service images into the Minikube image cache
- deploying with
kubectl apply -k k8n - exposing services
Once the stack is up:
curl http://localhost:8082/actuator/health
curl http://localhost:8081/actuator/healthBase URL:
http://localhost:8082
Swagger UI:
http://localhost:8082/swagger
OpenAPI document:
http://localhost:8082/api-docs
Endpoints:
POST /api/v1/urls
Request body:
{
"url": "https://example.com/some/long/path"
}Response:
"abc123"GET /api/v1/urls?shortUrl=abc123
Response:
"https://example.com/some/long/path"GET /api/v1/redirect/{shortCode}
Behavior:
- Returns
302 Found - Sets the
Locationheader to the original URL - Publishes a click event to Kafka for downstream analytics processing
GET /api/v1/admin/caches/status
POST /api/v1/admin/caches/clear-all
POST /api/v1/admin/caches/clear/{cacheName}
POST /api/v1/admin/caches/warmup
POST /api/v1/admin/caches/warmup/{limit}
Base URL:
http://localhost:8081
Swagger UI:
http://localhost:8081/swagger
OpenAPI document:
http://localhost:8081/api-docs
Current behavior:
- Consumes Kafka topic
url-click-events-topic - Stores click events in the analytics database
- Exposes operational endpoints such as
/actuator/healthand/actuator/prometheus
There is currently no public analytics query REST endpoint implemented in this module.