A production-grade, cloud-native microservices platform for healthcare patient management
- Overview
- Architecture
- Services
- Technology Stack
- Key Features
- Getting Started
- API Reference
- Inter-Service Communication
- Observability & Monitoring
- Cloud Infrastructure (AWS CDK)
- Integration Testing
- Project Structure
The Patient Management System is a fully containerized, event-driven microservices backend built with Java 21 and Spring Boot 3.4. It demonstrates real-world enterprise patterns including asynchronous messaging, synchronous RPC, API gateway routing with JWT authentication, Redis caching, distributed rate limiting, circuit breaking with fallback strategies, Prometheus metrics, and cloud-native deployment via AWS CDK with LocalStack.
This project serves as a comprehensive reference for:
- Designing and implementing a microservices architecture from scratch
- Applying resilience patterns (circuit breaker, retry, fallback)
- Implementing event-driven communication with Apache Kafka and Protobuf
- Securing a distributed system with JWT authentication at the gateway layer
- Deploying to AWS using Infrastructure as Code with the CDK
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β External Client β
ββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββ
β HTTP :4004
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β API Gateway (port 4004) β
β β’ JWT Validation Filter β’ Rate Limiting (Redis) β
β β’ Route to Auth Service β’ Route to Patient Serviceβ
ββββ¬βββββββββββββββββββββββββββββββββββββββ¬βββββββββββββ
β /auth/** β /api/patients/**
βΌ βΌ
ββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ
β Auth Service :4005 β β Patient Service :4000 β
β β’ JWT login/validate β β β’ CRUD operations β
β β’ PostgreSQL DB βββvalidateβ β’ Redis cache (10 min TTL) β
ββββββββββββββββββββββββββββ β β’ Prometheus metrics β
βββββββββ¬βββββββββββ¬ββββββββββββ
gRPC :9001 β β Kafka
βΌ βΌ
βββββββββββββββββββββ βββββββββββββββββββββββββββββ
β Billing Service β β Apache Kafka Broker β
β :4001 / :9001 β β Topics: β
β β’ gRPC server β β patient.created β
β β’ Kafka consumer β β patient.updated β
β (billing-acct) β β billing-account β
βββββββββββββββββββββ ββββββββββββ¬βββββββββββββββββ
β Consumers
βββββββββββββββΌββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Appointment β β Analytics β β Billing β
β Service β β Service β β Service β
β :4003 β β :4002 β β (fallback) β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
ββββββββββββββββββββββββββββββββββ
β Monitoring Stack β
β Prometheus :9090 β
β Grafana :3000 β
ββββββββββββββββββββββββββββββββββ
| Service | Port | Description |
|---|---|---|
| API Gateway | 4004 |
Spring Cloud Gateway β JWT authentication, IP-based rate limiting, reverse proxy routing |
| Auth Service | 4005 |
User login, JWT token issuance and validation, Spring Security + PostgreSQL |
| Patient Service | 4000 |
Core CRUD for patients, Redis caching, Kafka producer, gRPC client |
| Billing Service | 4001 / 9001 |
gRPC server for account creation, Kafka consumer for fallback billing events |
| Appointment Service | 4003 |
Appointment scheduling with optimistic locking, Kafka consumer for patient cache |
| Analytics Service | 4002 |
Event listener for patient events (extensible analytics hooks) |
| Infrastructure | β | AWS CDK stack defining the full cloud topology (ECS Fargate, RDS, ElastiCache, MSK) |
| Monitoring | 9090 / 3000 |
Prometheus scraping + Grafana dashboards |
| Category | Technology |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.4, Spring Cloud Gateway |
| Messaging | Apache Kafka + Protocol Buffers (Protobuf) |
| RPC | gRPC (grpc-java 1.69, protobuf-maven-plugin) |
| Caching | Redis (Spring Data Redis, @Cacheable with 10-min TTL) |
| Databases | PostgreSQL (production), H2 (integration tests) |
| Security | Spring Security, JWT (JJWT 0.12.6) |
| Resilience | Resilience4j (Circuit Breaker + Retry) |
| Observability | Spring Actuator, Micrometer, Prometheus, Grafana |
| API Docs | SpringDoc OpenAPI (Swagger UI) |
| Cloud / IaC | AWS CDK (Java), LocalStack |
| Testing | JUnit 5, REST Assured |
| Containerization | Docker (per-service Dockerfiles) |
- JWT-based auth issued by the Auth Service and validated at the gateway
- Admin seed user pre-loaded via
data.sqlfor immediate use - Token extracted from the
Authorization: Bearer <token>header at every protected route
- Single entry point for all external traffic on port
4004 - JWT Validation Filter β custom
GatewayFilterFactorythat delegates validation to the Auth Service viaWebClient - IP-based Rate Limiting β backed by Redis; limits to 5 requests/second per IP (burst capacity: 5)
- Route profiles:
application.yml(local Docker) andapplication-prod.yml(AWS ECS with service discovery)
- Full CRUD API (
GET,POST,PUT,DELETE) with Swagger UI documentation - Pagination, sorting, and search (case-insensitive name search) with page metadata in the response
- Bean Validation with custom validation groups (
CreatePatientValidationGroup) βregisteredDateis only required on creation - Redis caching on
getPatients(key =page-size-sort-sortField, bypassed when a search value is active), TTL 10 minutes - AOP metrics β custom
custom.redis.cache.missPrometheus counter instrumented via a Spring AOP aspect aroundgetPatients
Events are serialized using Protocol Buffers for schema-safe, binary-efficient messaging:
| Topic | Producer | Consumers |
|---|---|---|
patient.created |
Patient Service | Appointment Service, Analytics Service |
patient.updated |
Patient Service | Appointment Service |
billing-account |
Patient Service (fallback) | Billing Service |
When a patient is created:
- Patient record saved to PostgreSQL
- gRPC call to Billing Service to create a billing account
patient.createdKafka event emitted- If gRPC fails β Circuit Breaker opens β fallback sends
billing-accountKafka event for async retry
- Billing Service exposes a gRPC server (
BillingGrpcService) on port9001 - Patient Service connects as a gRPC client (
BillingServiceGrpcClient) configured viabilling.service.addressandbilling.service.grpc.port - Contracts defined in
.protofiles and code-generated viaprotobuf-maven-plugin
@CircuitBreaker(name="billingService")β wraps the gRPC call; opens after repeated failures@Retry(name="billingRetry")β retries transiently failed gRPC calls- Fallback β
billingFallback()emits abilling-accountKafka event so billing account creation is eventually handled asynchronously - Billing Service Kafka consumer then picks this up and creates the account
- Local
CachedPatiententity updated via Kafka events β no cross-service HTTP calls needed to resolve patient names - Optimistic locking via JPA
@Versionon theAppointmententity to prevent concurrent update conflicts - Query by date range:
findByStartTimeBetween(from, to)
- Prometheus scrapes
/actuator/prometheusfrom the Patient Service every 5 seconds - Grafana (
grafana/grafana) deployed alongside for dashboard visualization - Custom Micrometer counter
custom.redis.cache.misstracks cache miss rate
| Tool | Version |
|---|---|
| Docker & Docker Compose | Latest |
| Java JDK | 21 |
| Maven | 3.9+ |
git clone <repository-url>
cd cloud-native-patient-management-systemStart the required infrastructure containers (PostgreSQL, Kafka, Redis):
docker-compose up -dMake sure Kafka, Redis, and both PostgreSQL databases are running before starting any Spring Boot service.
Each service is an independent Spring Boot application. Run them in this recommended order:
# 1. Auth Service
cd auth-service && ./mvnw spring-boot:run
# 2. Billing Service (must be up before Patient Service)
cd billing-service && ./mvnw spring-boot:run
# 3. Patient Service
cd patient-service && ./mvnw spring-boot:run
# 4. Appointment Service
cd appointment-service && ./mvnw spring-boot:run
# 5. Analytics Service
cd analytics-service && ./mvnw spring-boot:run
# 6. API Gateway (last β depends on all services)
cd api-gateway && ./mvnw spring-boot:runThe Auth Service's data.sql automatically seeds a test admin user on startup:
| Field | Value |
|---|---|
testuser@test.com |
|
| Password | password123 |
| Role | ADMIN |
All requests to protected endpoints must go through the API Gateway on port 4004.
POST http://localhost:4004/auth/login
Content-Type: application/json
{
"email": "testuser@test.com",
"password": "password123"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiJ9..."
}GET http://localhost:4004/auth/validate
Authorization: Bearer <token>All patient endpoints require a valid Authorization: Bearer <token> header.
GET http://localhost:4004/api/patients?page=1&size=10&sort=asc&sortField=name&searchValue=
Authorization: Bearer <token>Query Parameters:
| Parameter | Default | Description |
|---|---|---|
page |
1 |
Page number (1-indexed) |
size |
10 |
Page size |
sort |
asc |
Sort direction (asc | desc) |
sortField |
name |
Field to sort by |
searchValue |
"" |
Case-insensitive name search |
Response:
{
"patients": [
{
"id": "uuid",
"name": "John Doe",
"email": "john@example.com",
"address": "123 Main Street",
"dateOfBirth": "1990-01-15",
"registeredDate": "2024-01-01"
}
],
"currentPage": 1,
"pageSize": 10,
"totalPages": 5,
"totalElements": 47
}POST http://localhost:4004/api/patients
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Jane Smith",
"email": "jane.smith@example.com",
"address": "456 Oak Avenue",
"dateOfBirth": "1985-06-22",
"registeredDate": "2025-01-10"
}On creation, a billing account is automatically provisioned via gRPC to the Billing Service, and a
patient.createdevent is published to Kafka.
PUT http://localhost:4004/api/patients/{id}
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Jane Smith Updated",
"email": "jane.updated@example.com",
"address": "789 Pine Road",
"dateOfBirth": "1985-06-22"
}DELETE http://localhost:4004/api/patients/{id}
Authorization: Bearer <token>GET http://localhost:4003/appointments?from=2025-01-01T00:00:00&to=2025-12-31T23:59:59| Service | URL |
|---|---|
| Patient Service | http://localhost:4000/swagger-ui.html |
| Auth Service | http://localhost:4005/swagger-ui.html |
| Via Gateway (aggregated) | http://localhost:4004/api-docs/patients |
When a new patient is created, the Patient Service makes a synchronous gRPC call to provision a billing account:
Patient Service ββgRPCβββΊ Billing Service
createBillingAccount(patientId, name, email)
βββββββββββββββββββ
BillingResponse { accountId, status }
Failure Scenario (Circuit Breaker):
Patient Service ββgRPCβββΊ Billing Service (DOWN)
βΌ Circuit Opens
Patient Service ββKafkaβββΊ billing-account topic
βΌ
Billing Service Kafka Consumer picks up the event
and creates the billing account asynchronously
| Event | Schema (Protobuf) | Produced By | Consumed By |
|---|---|---|---|
patient.created |
PatientEvent { patientId, name, email } |
Patient Service | Appointment Service, Analytics Service |
patient.updated |
PatientEvent { patientId, name, email } |
Patient Service | Appointment Service |
billing-account |
BillingAccountEvent { patientId, name, email, eventType } |
Patient Service (fallback) | Billing Service |
The Appointment Service maintains a local CachedPatient table β it subscribes to patient.created and patient.updated events and upserts the local cache so that appointment queries can resolve patient names without any synchronous API call.
The Patient Service exposes metrics at /actuator/prometheus. Prometheus is configured to scrape this endpoint every 5 seconds.
| Metric | Description |
|---|---|
| Standard Spring Boot metrics | JVM, HTTP requests, DB pool, etc. |
custom.redis.cache.miss |
Custom AOP counter β tracks how often the getPatients service method bypasses the Redis cache |
Grafana runs on port 3000. Connect it to Prometheus (http://prometheus:9090) and import dashboards for:
- JVM health (heap, GC, threads)
- HTTP request rates and latencies
- Custom cache miss counters
Spring Boot Actuator health endpoints are available on each service at /actuator/health.
The infrastructure/ module contains a Java-based AWS CDK stack (LocalStack.java) that provisions the entire system to AWS (and locally via LocalStack).
| Resource | AWS Service | Config |
|---|---|---|
| VPC | Amazon VPC | 2 Availability Zones |
| Auth DB | Amazon RDS PostgreSQL 17.2 | db.t2.micro, auto-generated secret |
| Patient DB | Amazon RDS PostgreSQL 17.2 | db.t2.micro, auto-generated secret |
| Kafka Cluster | Amazon MSK | kafka.m5.xlarge, 2 broker nodes |
| Redis Cluster | Amazon ElastiCache | cache.t2.micro, 1 node |
| ECS Cluster | Amazon ECS | Fargate, Cloud Map service discovery |
| API Gateway | ECS Fargate + ALB | Public-facing, port 4004 |
| Auth Service | ECS Fargate | Private, port 4005 |
| Patient Service | ECS Fargate | Private, port 4000 |
| Billing Service | ECS Fargate | Private, ports 4001 + 9001 |
| Analytics Service | ECS Fargate | Private, port 4002 |
| Prometheus | ECS Fargate | Private, port 9090 |
| Grafana | ECS Fargate + ALB | Public-facing, port 3000 |
cd infrastructure
./localstack-deploy.shRequires LocalStack and the AWS CDK to be installed.
In production (ECS), services communicate via AWS Cloud Map private DNS:
auth-service.patient-management.local:4005patient-service.patient-management.local:4000billing-service.patient-management.local:9001
The integration-tests/ module contains end-to-end tests powered by REST Assured that run against the full stack.
# Start all services first (API Gateway must be reachable on :4004)
cd integration-tests
./mvnw test| Test | Description |
|---|---|
shouldReturnPatientsWithValidToken |
Logs in, gets a JWT, calls GET /api/patients, asserts 200 with patients list |
shouldReturn429AfterLimitExceeded |
Fires 10 rapid requests and asserts at least 1 returns HTTP 429 (rate limit enforced) |
AuthIntegrationTest |
Auth flow validation tests |
cloud-native-patient-management-system/
β
βββ api-gateway/ # Spring Cloud Gateway
β βββ src/main/
β βββ java/com/pm/apigateway/
β β βββ config/RateLimiterConfig.java # IP-based key resolver
β β βββ filter/JwtValidationGatewayFilterFactory.java
β βββ resources/
β βββ application.yml # Local Docker routes
β βββ application-prod.yml # AWS ECS routes
β
βββ auth-service/ # JWT Authentication
β βββ src/main/java/com/pm/authservice/
β βββ controller/AuthController.java # POST /login, GET /validate
β βββ service/AuthService.java
β βββ service/UserService.java
β βββ util/JwtUtil.java
β
βββ patient-service/ # Core Patient Domain
β βββ src/main/java/com/pm/patientservice/
β βββ aspects/PatientServiceMetrics.java # AOP Prometheus counter
β βββ cache/RedisCacheConfig.java # Redis TTL & serialization
β βββ controller/PatientController.java # CRUD REST endpoints
β βββ grpc/BillingServiceGrpcClient.java # gRPC client + circuit breaker
β βββ kafka/KafkaProducer.java # Protobuf event publisher
β βββ model/Patient.java
β βββ service/PatientService.java
β
βββ billing-service/ # Billing Account Management
β βββ src/main/java/com/pm/billingservice/
β βββ grpc/BillingGrpcService.java # gRPC server implementation
β βββ kafka/KafkaConsumer.java # Fallback billing event consumer
β
βββ appointment-service/ # Appointment Scheduling
β βββ src/main/java/com/pm/appointmentservice/
β βββ controller/AppointmentController.java
β βββ entity/Appointment.java # @Version optimistic locking
β βββ entity/CachedPatient.java # Local patient data cache
β βββ kafka/KafkaConsumer.java # Syncs patient cache from events
β βββ service/AppointmentService.java
β
βββ analytics-service/ # Event Analytics
β βββ src/main/java/com/pm/analyticsservice/
β βββ kafka/KafkaConsumer.java # Patient event subscriber
β
βββ infrastructure/ # AWS CDK Stack (Java)
β βββ src/main/java/com/pm/stack/LocalStack.java # Full AWS topology definition
β
βββ monitoring/
β βββ prometheus.yml # Scrape config for patient-service
β βββ prometheus-prod.yml # Production scrape config
β
βββ integration-tests/ # End-to-End Tests (REST Assured)
β βββ src/test/java/
β βββ PatientIntegrationTest.java
β βββ AuthIntegrationTest.java
β
βββ api-requests/ # HTTP request files (IntelliJ / VS Code)
β βββ auth-service/ # login.http, validate.http
β βββ patient-service/ # create, update, delete, get patients
β βββ appointment-service/ # get-appointments-by-date-range.http
β
βββ grpc-requests/
βββ billing-service/ # gRPC test requests
| Variable | Example | Description |
|---|---|---|
SPRING_DATASOURCE_URL |
jdbc:postgresql://auth-service-db:5432/db |
PostgreSQL connection |
SPRING_DATASOURCE_USERNAME |
admin_user |
DB username |
SPRING_DATASOURCE_PASSWORD |
password |
DB password |
SPRING_JPA_HIBERNATE_DDL_AUTO |
update |
Schema management |
SPRING_SQL_INIT_MODE |
always |
Run data.sql on startup |
| Variable | Example | Description |
|---|---|---|
SPRING_DATASOURCE_URL |
jdbc:postgresql://patient-service-db:5432/db |
PostgreSQL connection |
SPRING_DATASOURCE_USERNAME |
admin_user |
DB username |
SPRING_DATASOURCE_PASSWORD |
password |
DB password |
SPRING_KAFKA_BOOTSTRAP_SERVERS |
kafka:9092 |
Kafka broker address |
BILLING_SERVICE_ADDRESS |
billing-service |
gRPC host for Billing Service |
BILLING_SERVICE_GRPC_PORT |
9001 |
gRPC port for Billing Service |
SPRING_JPA_HIBERNATE_DDL_AUTO |
update |
Schema management |
SPRING_SQL_INIT_MODE |
always |
Run data.sql on startup |
| Variable | Example | Description |
|---|---|---|
SPRING_PROFILES_ACTIVE |
prod |
Activate production route config |
AUTH_SERVICE_URL |
http://auth-service:4005 |
Auth Service URL for JWT validation |
REDIS_HOST |
redis |
Redis hostname for rate limiting |
REDIS_PORT |
6379 |
Redis port |
This project is for educational purposes. Original course material by Chris Blakely available on his YouTube channel.
Made with β€οΈ using Java, Spring Boot, and modern distributed systems patterns