Full-Stack Demo: Java 17 + Spring Boot 3 + gRPC + Kafka + Redis + PostgreSQL + Vue 3 + React 18
A production-ready microservices architecture demonstrating enterprise patterns for distributed systems, event-driven communication, and modern frontend development.
This repository is a demonstration system, not a production-ready platform.
Its purpose is to showcase:
- Microservice decomposition and service boundaries
- Event-driven communication using Kafka
- Independent frontend ownership (React and Vue)
- Data consistency and failure considerations in distributed systems
Trade-offs such as infrastructure hardening, security depth, and full observability are intentionally simplified to keep the focus on architectural concepts.
- Architecture Overview
- Key Design Decisions
- Tech Stack
- Service Breakdown
- Communication Patterns
- Quick Start
- API Documentation
- Testing Strategy
- Frontend Applications
- Interview Talking Points
This system is intentionally split into microservices to demonstrate:
- Independent data ownership per service
- Asynchronous communication via Kafka
- Eventual consistency between domains
- Failure isolation at service boundaries
In a real production system with this scope, a modular monolith could be sufficient. Microservices were chosen here solely to demonstrate distributed-system concerns commonly discussed in senior backend interviews.
Kafka is used to demonstrate:
- Event-driven architecture
- Loose coupling between services
- Asynchronous propagation of domain events
- Replayability and auditability of state changes
Kafka is not used for simple request/response workflows. Synchronous REST is reserved for query-style interactions, while state changes are propagated through events.
Each service emits domain events that represent facts, not commands.
Examples:
- OrderCreated
- InventoryReserved
- ProductPriceUpdated
Rules:
- Events are immutable
- Events describe something that already happened
- Consumers must be idempotent
This system assumes:
- At-least-once delivery from Kafka
- Eventual consistency between services
- Idempotent consumers to avoid duplicate processing
Failure scenarios considered:
- Consumer restart and replay
- Temporary Kafka unavailability
- Partial service outages
Compensation is preferred over distributed transactions. No two-phase commit is used.
Two frontends exist to demonstrate:
- Independent UI ownership per domain
- Technology-agnostic backend APIs
- Parallel evolution of user interfaces
In production, standardizing on one framework may be preferable. Here, diversity is intentional to show backend resilience to UI changes.
To keep the scope focused, this project intentionally omits:
- Centralized authentication/authorization
- Distributed tracing and metrics
- Advanced schema registry enforcement
- Production-grade security hardening
These concerns are well-understood but excluded to avoid obscuring the core architectural demonstrations.
These small, focused improvements add senior-level signals without bloating the codebase:
- Add idempotency checks (or comments) in Kafka consumers
- Add
eventVersionto events for evolution and compatibility - Explicitly state
service-owned databaserule in README - Add an ASCII event-flow diagram showing producers → topics → consumers
┌─────────────────────────────────────────────────────────────────────────────┐
│ FRONTEND LAYER │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Vue 3 + Vite │ │ React 18 + Vite │ │
│ │ (Port 5173) │ │ (Port 5174) │ │
│ │ • Composition API │ │ • Redux Toolkit │ │
│ │ • Reactive Store │ │ • Custom Hooks │ │
│ └──────────┬──────────┘ └──────────┬──────────┘ │
│ │ │ │
│ └──────────────┬─────────────────────┘ │
│ │ REST/HTTP │
└────────────────────────────┼────────────────────────────────────────────────┘
│
┌────────────────────────────┼────────────────────────────────────────────────┐
│ ▼ API GATEWAY / PROXY │
│ Vite Dev Proxy (routes /api/* to services) │
└────────────────────────────┬────────────────────────────────────────────────┘
│
┌────────────────────────────┼────────────────────────────────────────────────┐
│ BACKEND SERVICES │
│ │ │
│ ┌─────────────────────────┼─────────────────────────────────┐ │
│ │ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ PRODUCTS │ │ ORDERS │ │ INVENTORY │ │ │
│ │ │ SERVICE │ │ SERVICE │ │ SERVICE │ │ │
│ │ │ (Port 8080) │ │ (Port 8081) │ │ (Port 8082) │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • REST API │ │ • REST API │ │ • REST API │ │ │
│ │ │ • JWT Auth │ │ • JWT Auth │ │ • gRPC Server│ │ │
│ │ │ • Redis Cache│ │ • gRPC Client│ │ (Port 9090)│ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │ │
│ │ │ │ gRPC (sync) │ │ │
│ │ │ └──────────────────┘ │ │
│ │ │ │ │
│ └─────────┼──────────────────────────────────────────────────┘ │
│ │ │
│ │ Kafka (async events) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ APACHE KAFKA │ │
│ │ Topics: │ │
│ │ • products.product-created.v1 (Products → Inventory) │ │
│ │ • inventory.stock-reserved.v1 (Inventory → Orders) │ │
│ │ • inventory.low-stock.v1 (Inventory → Products) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
│
┌────────────────────────────┼────────────────────────────────────────────────┐
│ DATA LAYER │
│ │ │
│ ┌─────────────────┐ ┌────┴────────┐ ┌─────────────────┐ │
│ │ PostgreSQL │ │ Redis │ │ H2 In-Memory │ │
│ │ (Port 5432) │ │ (Port 6379) │ │ (Orders/Inv) │ │
│ │ │ │ │ │ │ │
│ │ • Products DB │ │ • API Cache │ │ • Dev/Test DB │ │
│ │ • Persistent │ │ • Sessions │ │ • Fast Startup │ │
│ └─────────────────┘ └─────────────┘ └─────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
| Decision | Rationale | Enterprise Benefit |
|---|---|---|
| gRPC for sync operations | Stock reservation requires immediate consistency | Sub-millisecond latency, type-safe contracts, bidirectional streaming ready |
| Kafka for async events | Decoupled services, event replay, audit trail | Horizontal scaling, fault tolerance, event sourcing ready |
| Redis caching | Reduce database load on high-traffic endpoints | 10-100x faster reads, distributed cache for multi-instance |
| Shared Protobuf contracts | Single source of truth for service interfaces | No API drift, generated clients, versioned schemas |
| PostgreSQL + H2 hybrid | Production-ready persistence + fast dev cycle | Real DB for catalog, in-memory for rapid iteration |
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PRODUCTS │ │ ORDERS │ │ INVENTORY │
│ BOUNDED │ │ BOUNDED │ │ BOUNDED │
│ CONTEXT │ │ CONTEXT │ │ CONTEXT │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ Owns: │ │ Owns: │ │ Owns: │
│ • Product name │ │ • Order status │ │ • Stock levels │
│ • Description │ │ • Customer │ │ • Reservations │
│ • Price │ │ • Line items │ │ • Thresholds │
│ • Catalog data │ │ • Totals │ │ • Availability │
└─────────────────┘ └─────────────────┘ └─────────────────┘
| Technology | Version | Purpose |
|---|---|---|
| Java | 17 LTS | Core language with modern features (records, sealed classes, pattern matching) |
| Spring Boot | 3.x | Application framework, dependency injection, auto-configuration |
| Spring Data JPA | 3.x | Repository pattern, Hibernate ORM |
| Spring Security | 6.x | JWT authentication, role-based access control |
| Spring Kafka | 3.x | Event-driven messaging |
| gRPC Java | 1.62 | High-performance RPC framework |
| Protobuf | 3.25 | Schema-first API contracts |
| PostgreSQL | 16 | Production database (products catalog) |
| H2 | 2.x | In-memory database (orders, inventory for dev) |
| Redis | 7 | Distributed caching layer |
| Technology | Version | Purpose |
|---|---|---|
| Vue 3 | 3.x | Reactive UI with Composition API |
| React | 18 | Component-based UI with hooks |
| Vite | 5.x | Lightning-fast build tool |
| Redux Toolkit | 2.x | State management (React) |
| TailwindCSS | 3.x | Utility-first styling |
| Technology | Purpose |
|---|---|
| Docker Compose | Local development orchestration |
| Apache Kafka | Event streaming platform |
| Maven | Build automation, dependency management |
Responsibilities: Product catalog management, caching, authentication
Key Features:
- ✅ Full CRUD with pagination, sorting, search
- ✅ JWT authentication (HS256, 24h expiry)
- ✅ Redis caching with
@Cacheable,@CacheEvict,@CachePut - ✅ Publishes
ProductCreatedEventto Kafka - ✅ Consumes
LowStockEventfor dashboard projection
API Endpoints:
GET /api/products # List all (+ pagination)
GET /api/products/{id} # Get single product
POST /api/products # Create (requires JWT)
PUT /api/products/{id} # Full update
PATCH /api/products/{id} # Partial update
DELETE /api/products/{id} # Delete
GET /api/products/search?keyword=x # Full-text search
POST /api/auth/login # Get JWT token
POST /api/auth/register # Create user
Responsibilities: Order lifecycle, stock reservation orchestration
Key Features:
- ✅ Order creation with line items
- ✅ gRPC client calls Inventory for stock reservation
- ✅ Status transitions: CREATED → RESERVED → CANCELLED
- ✅ Publishes order events to Kafka
API Endpoints:
GET /api/orders # List all orders
GET /api/orders/{id} # Get single order
POST /api/orders # Create order
POST /api/orders/{id}/reserve # Reserve stock (gRPC → Inventory)
POST /api/orders/{id}/cancel # Cancel order
gRPC Flow (Reserve Stock):
┌──────────────┐ gRPC (sync) ┌──────────────┐
│ Orders │ ─────────────────▶ │ Inventory │
│ Service │ ReserveStock() │ Service │
│ │ ◀───────────────── │ │
│ │ Success/Fail │ │
└──────────────┘ └──────────────┘
Responsibilities: Stock management, reservation logic, low-stock alerts
Key Features:
- ✅ gRPC server for synchronous stock reservation
- ✅ Atomic stock updates with optimistic locking
- ✅ Low-stock threshold alerts (publishes to Kafka)
- ✅ Consumes
ProductCreatedEventto auto-create stock entries
gRPC Contract (contracts/src/main/proto/inventory.proto):
service InventoryService {
rpc ReserveStock(ReserveStockRequest) returns (ReserveStockResponse);
}
message ReserveStockRequest {
int64 order_id = 1;
repeated ReserveItem items = 2;
}
message ReserveStockResponse {
bool reserved = 1;
string reason = 2;
}Use case: Stock reservation requires immediate consistency
User → Orders Service → gRPC → Inventory Service → Response
(blocking, ~50ms)
Why gRPC?
- Type-safe contracts (Protobuf)
- ~10x faster than REST
- Streaming-ready for future features
Use case: Notify services after state changes
Products Service ──ProductCreatedEvent──▶ Kafka ──▶ Inventory Service
Inventory Service ──LowStockEvent────────▶ Kafka ──▶ Products Service
Why Kafka?
- Services remain decoupled
- Events can be replayed
- Multiple consumers possible
Use case: High-traffic read endpoints
First request: Cache MISS → PostgreSQL → Store in Redis → Return
Second request: Cache HIT → Return from Redis (microseconds)
- Docker Desktop
- Java 17+
- Node.js 20+
# Clone the repo
git clone https://github.com/aszender/java-spring-Guide.git
cd java-spring-Guide
# Start everything (Postgres + Kafka + Redis + services + UIs)
WITH_KAFKA=1 WITH_REDIS=1 ./dev.sh up
# Check status
./dev.sh status
# Stop everything
./dev.sh down| Command | What it starts |
|---|---|
./dev.sh up |
Basic mode (Postgres + services + UIs) |
WITH_KAFKA=1 ./dev.sh up |
+ Kafka event streaming |
WITH_REDIS=1 ./dev.sh up |
+ Redis caching |
WITH_KAFKA=1 WITH_REDIS=1 ./dev.sh up |
Full production-like setup |
| Service | URL |
|---|---|
| Vue UI | http://localhost:5173 |
| React UI | http://localhost:5174 |
| Products API | http://localhost:8080/api/products |
| Orders API | http://localhost:8081/api/orders |
| Inventory API | http://localhost:8082/api/inventory/stock |
# 1. Login to get JWT token
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
# 2. Use token for protected endpoints
curl -X POST http://localhost:8080/api/products \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"MacBook Pro","description":"16-inch","price":2499.99}'# Step 1: Create a product (triggers Kafka → Inventory creates stock)
curl -X POST http://localhost:8080/api/products \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"iPhone 15","price":999.99}'
# Step 2: Check inventory was auto-created
curl http://localhost:8082/api/inventory/stock
# Step 3: Create an order
curl -X POST http://localhost:8081/api/orders \
-H "Content-Type: application/json" \
-d '{"customerName":"John Doe","items":[{"productId":1,"quantity":2,"unitPrice":999.99}]}'
# Step 4: Reserve stock (gRPC call)
curl -X POST http://localhost:8081/api/orders/1/reserve
# Step 5: Check order status
curl http://localhost:8081/api/orders/1 ┌───────────┐
│ E2E │ ← Manual testing via curl/UI
│ (few) │
┌────┴───────────┴────┐
│ Integration │ ← @SpringBootTest + @Transactional
│ (some) │
┌────┴─────────────────────┴────┐
│ Unit Tests │ ← @ExtendWith(MockitoExtension)
│ (many) │ @WebMvcTest
└───────────────────────────────┘
Located in products-service/src/test/java/
| Test Class | Type | What it Tests |
|---|---|---|
ProductServiceTest |
Unit | Service layer logic with mocked repository |
ProductControllerWebMvcTest |
Slice | REST endpoints with @WebMvcTest + MockMvc |
ProductRepositoryTest |
Integration | JPA queries with real database (@SpringBootTest) |
# Run all tests (products-service)
cd products-service && ./mvnw test
# Run with verbose output
./mvnw test -Dtest=ProductServiceTest
# Run only unit tests (fast)
./mvnw test -Dgroups="unit"
# Run all services tests
./mvnw test -pl products-service,orders-service,inventory-service| Tool | Purpose |
|---|---|
| JUnit 5 | Test framework with @Test, @BeforeEach, @DisplayName |
| Mockito | Mocking dependencies (@Mock, @InjectMocks, when().thenReturn()) |
| AssertJ | Fluent assertions (assertThat(x).isEqualTo(y)) |
| MockMvc | Test REST controllers without starting server |
| @SpringBootTest | Full integration tests with real beans |
| @WebMvcTest | Slice test for web layer only |
| @DataJpaTest | Slice test for repository layer |
| H2 | In-memory database for fast integration tests |
- ✅ Happy path - CRUD operations work correctly
- ✅ Edge cases - Not found, validation errors
- ✅ Exception handling -
ProductNotFoundExceptionthrown appropriately - ✅ Repository queries - Custom JPQL/native queries return expected results
- Composition API with
<script setup> - Reactive Store pattern (singleton module)
- Composables for reusable logic
- Pages: Products, Orders, Inventory
- Redux Toolkit for state management
- Custom Hooks wrapping Redux
- Feature-based folder structure
- Pages: Products, Orders, Inventory
java-spring/
├── products-service/ # Product catalog + Auth + Cache
├── orders-service/ # Order management + gRPC client
├── inventory-service/ # Stock management + gRPC server
├── contracts/ # Shared Protobuf definitions
├── frontVUE/products-ui/ # Vue 3 frontend
├── frontReact/products-react/ # React 18 frontend
├── basic/ # Java fundamentals demos
├── docker-compose.yml # Infrastructure
├── dev.sh # Startup script
└── pom.xml # Parent Maven POM
- Centralized with
@RestControllerAdvice(GlobalExceptionHandler) - Handles validation errors, not found, bad JSON, data integrity, and unexpected exceptions
- Returns structured JSON error responses (status, message, path, validation details)
- Uses Jakarta Bean Validation (
@Valid,@NotNull, etc.) on DTOs and controller inputs - Automatic validation of incoming requests
- Validation errors are caught and returned as clear API error responses
- Spring Boot logging enabled (Logback)
- Log levels set in
application.properties(INFOfor root,DEBUGfor web) - All requests, errors, and stack traces are logged for troubleshooting
This project currently runs locally with Docker Compose. For production deployment, the architecture would include:
- ECS/Fargate: Containerized microservices with auto-scaling
- RDS PostgreSQL: Multi-AZ deployment for high availability
- ElastiCache Redis: Distributed caching layer
- MSK (Managed Kafka): Managed event streaming
- Application Load Balancer: Traffic distribution across service instances
- ECR: Private container registry
- S3 + CloudFront: Static site hosting with global CDN
- Vue/React build artifacts deployed to S3 buckets
- CloudFront for edge caching and HTTPS
- Route 53 for custom domain management
- Reduced latency with edge locations worldwide
- API Gateway (optional): Single entry point for backend APIs with rate limiting and authentication
pipeline {
stages {
stage('Backend Build & Test') {
steps {
sh './mvnw clean test package'
}
}
stage('Frontend Build') {
steps {
sh 'cd frontVUE && npm run build'
sh 'cd frontReact && npm run build'
}
}
stage('Deploy Frontend to S3') {
steps {
sh 'aws s3 sync frontVUE/dist/ s3://products-vue-app/'
sh 'aws s3 sync frontReact/dist/ s3://products-react-app/'
sh 'aws cloudfront create-invalidation --distribution-id XXX'
}
}
stage('Deploy Backend to ECS') {
steps {
sh 'docker build -t products-service .'
sh 'docker push ${ECR_REPO}/products-service:${BUILD_NUMBER}'
sh 'aws ecs update-service --cluster prod --service products --force-new-deployment'
}
}
}
}Scalability:
- Stateless services enable horizontal scaling via ECS auto-scaling groups
- CloudFront CDN reduces backend load for static assets
- Database read replicas for Products service (read-heavy workload)
- Kafka topic partitioning for parallel event processing
- Redis cluster mode for distributed caching
Security:
- VPC isolation for backend services (private subnets)
- Security groups restricting inter-service communication
- S3 bucket policies with CloudFront Origin Access Identity (OAI)
- Secrets Manager for database credentials and API keys
- WAF on CloudFront for DDoS protection
Reliability:
- Multi-AZ RDS deployment (automatic failover)
- ECS service auto-recovery on health check failures
- Circuit breakers (Resilience4j) for fault isolation
- Kafka consumer groups with retry logic and dead letter queues
- CloudWatch alarms for proactive monitoring
Cost Optimization:
- ECS Fargate Spot instances for non-critical workloads
- S3 lifecycle policies for log retention
- CloudFront caching reduces origin requests
- RDS reserved instances for predictable workloads
- CloudWatch Logs: Centralized logging for all services
- CloudWatch Metrics: CPU, memory, request latency dashboards
- X-Ray: Distributed tracing across microservices
- Alarms: Automated alerts for error rates, latency spikes, low disk space
MIT License
Andres Gonzalez