A production-ready e-commerce REST API built on a microservices architecture.
Domain isolation · JWT + internal token security · Circuit breakers · Distributed tracing
Overview · Architecture · Services · Security · API · Getting Started · Tech Stack
BookStore is a fully decomposed microservices backend for an online bookstore — covering user authentication, book catalog management, shopping cart, and order processing. Each domain is an independently deployable Spring Boot service with its own MySQL database, communicating over HTTP via OpenFeign with full fault tolerance.
Key design decisions:
- No shared databases — each service owns its data exclusively, enforcing true domain isolation
- Two-tier auth — JWT for external clients, a shared-secret internal token for service-to-service calls, managed through a
common-securityshared library - Resilience by default — every Feign client is wrapped in a Circuit Breaker + Retry chain via Resilience4j
- Schema-as-code — Liquibase manages all migrations; Hibernate is set to
validateonly - Full observability — Micrometer tracing exported to Zipkin, health and circuit breaker metrics via Actuator
- Complete domain isolation — no shared databases
- Two-tier security — JWT for clients + internal shared-secret token
- Resilience by design — every Feign client wrapped in Circuit Breaker + Retry (Resilience4j)
- Zero-downtime ready — independent services, Eureka, Config Server
- Full observability — distributed tracing (Zipkin) + Actuator + Micrometer
- Schema-as-code — Liquibase migrations, Hibernate
validateonly - Production-ready — Docker Compose support included
graph TD
subgraph Client["Client (HTTP/REST)"]
C[Browser / Postman / Mobile App]
end
C --> G[API Gateway<br/>:8765<br/>Spring Cloud Gateway<br/>Load Balanced]
G --> U[user-service<br/>:8001<br/>user_db]
G --> B[book-service<br/>:8002<br/>book_db]
G --> Cart[cart-service<br/>:8003<br/>cart_db]
G --> O[order-service<br/>:8004<br/>order_db]
U -.->|"Feign + X-Internal-Token<br/>Circuit Breaker"| Cart
Cart -.->|"Feign + CB + Retry"| B
O -.->|"Feign + CB + Retry"| Cart
subgraph Infrastructure["Infrastructure Services"]
E[Eureka<br/>:8761<br/>Service Discovery]
Config[Config Server<br/>:8888<br/>Git-backed]
Z[Zipkin<br/>:9411<br/>Distributed Tracing]
end
style G fill:#6DB33F,stroke:#fff,color:#fff
style U fill:#4479A1,stroke:#fff,color:#fff
style B fill:#4479A1,stroke:#fff,color:#fff
style Cart fill:#4479A1,stroke:#fff,color:#fff
style O fill:#4479A1,stroke:#fff,color:#fff
user-service ──[register: create cart]──────────────► cart-service
POST /cart/internal/{userId}
cart-service ──[add item: fetch book details]───────► book-service
GET /books/internal/{bookId}
order-service ──[create order: fetch cart]───────────► cart-service
GET /cart/internal/{userId}
order-service ──[after order: clear cart]────────────► cart-service
DELETE /cart/internal/{userId}/clear
All internal calls use X-Internal-Token header, are never reachable from the gateway, and are protected by circuit breakers with automatic fallbacks.
| Service | Port | Database | Responsibility |
|---|---|---|---|
| api-gateway | 8765 |
— | Single entry point, routing, load balancing |
| user-service | 8001 |
user_db |
Registration, login, JWT issuance |
| book-service | 8002 |
book_db |
Book catalog, categories, search |
| cart-service | 8003 |
cart_db |
Shopping cart and cart items |
| order-service | 8004 |
order_db |
Order lifecycle and order items |
| config-server | 8888 |
— | Centralized Git-backed configuration |
| naming-server | 8761 |
— | Eureka service registry |
| common-security | lib | — | Shared JWT + internal token library |
╔═══════════════════════════════════════════════════════════════╗
║ TIER 1 — External (client → gateway → service) ║
║ ║
║ Authorization: Bearer <JWT> ║
║ ║
║ • Issued by user-service on successful login ║
║ • Signed with HMAC-SHA256 using shared JWT_SECRET ║
║ • Contains: userId (Long), roles (List<String>) ║
║ • Verified independently by each service — no DB lookup ║
╚═══════════════════════════════════════════════════════════════╝
╔═══════════════════════════════════════════════════════════════╗
║ TIER 2 — Internal (service → service only) ║
║ ║
║ X-Internal-Token: <INTERNAL_TOKEN> ║
║ ║
║ • Fixed shared secret from environment, never exposed ║
║ • Only valid on /*/internal/** URL patterns ║
║ • Checked by InternalRequestFilter on every service ║
║ • Auto-injected into all Feign calls via ║
║ InternalTokenRequestInterceptor (Spring auto-config) ║
╚═══════════════════════════════════════════════════════════════╝
{
"sub": "user@example.com",
"userId": 42,
"roles": ["ROLE_USER"],
"iat": 1710000000,
"exp": 1710003600
}The userId claim is extracted by JwtAuthenticationFilter and stored as the Spring Security principal — controllers retrieve ownership directly from SecurityContextHolder with zero additional queries.
| Endpoint Group | ROLE_USER | ROLE_ADMIN |
|---|---|---|
| Read books / categories | ✅ | ✅ |
| Create / update / delete books | ❌ | ✅ |
| Manage own cart | ✅ | ✅ |
| Place and read own orders | ✅ | ✅ |
| Update any order status | ❌ | ✅ |
Base URL: http://localhost:8765
All endpoints except /api/auth/** require Authorization: Bearer <token>.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/auth/registration |
Public | Register — auto-creates shopping cart |
POST |
/api/auth/login |
Public | Login — returns signed JWT |
POST /api/auth/registration
// Request body
{
"email": "john@example.com",
"password": "SecurePass1",
"repeatPassword": "SecurePass1",
"firstName": "John",
"lastName": "Doe",
"shippingAddress": "123 Main St, New York"
}
// 201 Created
{
"id": 1,
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"shippingAddress": "123 Main St, New York"
}POST /api/auth/login
// Request body
{
"email": "john@example.com",
"password": "SecurePass1"
}
// 200 OK
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsInN1YiI6ImpvaG4..."
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/books |
USER / ADMIN | List all books (paginated) |
GET |
/api/books/{id} |
USER / ADMIN | Get book by ID |
GET |
/api/books/search |
USER / ADMIN | Search by title or author |
POST |
/api/books |
ADMIN | Create book |
PUT |
/api/books/{id} |
ADMIN | Update book |
DELETE |
/api/books/{id} |
ADMIN | Soft-delete book |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/categories |
USER / ADMIN | List all categories |
GET |
/api/categories/{id} |
USER / ADMIN | Get category by ID |
GET |
/api/categories/{id}/books |
USER / ADMIN | All books in a category |
POST |
/api/categories |
ADMIN | Create category |
PUT |
/api/categories/{id} |
ADMIN | Update category |
DELETE |
/api/categories/{id} |
ADMIN | Soft-delete category |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/cart |
USER / ADMIN | Get current user's cart |
POST |
/api/cart |
USER / ADMIN | Add book to cart |
PUT |
/api/cart/items/{id} |
USER / ADMIN | Update item quantity |
DELETE |
/api/cart/items/{id} |
USER / ADMIN | Remove item from cart |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/orders |
USER / ADMIN | Create order from current cart |
GET |
/api/orders |
USER / ADMIN | List current user's orders |
GET |
/api/orders/{id}/items |
USER / ADMIN | Get items in an order |
GET |
/api/orders/{id}/items/{itemId} |
USER / ADMIN | Get a specific order item |
PATCH |
/api/orders/{id} |
ADMIN | Update order status |
- Docker & Docker Compose
- Git
# 1. Clone
git clone https://github.com/YOUR_USERNAME/bookstore-microservices.git
cd bookstore-microservices
# 2. Configure environment
cp .env.example .env
# Edit .env — set a real JWT_SECRET (32+ chars) and INTERNAL_TOKEN
# 3. Start all services
docker compose up --buildStartup order is managed automatically via healthchecks.
Wait ~2 minutes for all services to become healthy.
# Register
curl -X POST http://localhost:8765/api/auth/registration \
-H "Content-Type: application/json" \
-d '{"email":"user@test.com","password":"Pass1234!","repeatPassword":"Pass1234!","firstName":"John","lastName":"Doe"}'
# Login
curl -X POST http://localhost:8765/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@test.com","password":"Pass1234!"}'docker compose down # keep data
docker compose down -v # also remove volumes| Service | URL |
|---|---|
| 🌐 API Gateway | http://localhost:8765 |
| 📋 Eureka Dashboard | http://localhost:8761 |
| ⚙️ Config Server | http://localhost:8888 |
| 🔍 Zipkin Tracing | http://localhost:9411 |
| 📄 Swagger (user-service) | http://localhost:8001/swagger-ui/index.html |
| 📄 Swagger (book-service) | http://localhost:8002/swagger-ui/index.html |
| 📄 Swagger (cart-service) | http://localhost:8003/swagger-ui/index.html |
| 📄 Swagger (order-service) | http://localhost:8004/swagger-ui/index.html |
Swagger UI is only available with the
devSpring profile active.
| Category | Technology |
|---|---|
| Language | Java 25 |
| Framework | Spring Boot 4.0.3 |
| Service Mesh | Spring Cloud 2025.1 |
| Security | Spring Security + JJWT 0.11.5 |
| Fault Tolerance | Resilience4j |
| Persistence | Spring Data JPA + Hibernate |
| Database | MySQL 8.0 |
| Migrations | Liquibase |
| Mapping | MapStruct 1.5.5 |
| Observability | Micrometer + Zipkin + Actuator |
| API Docs | SpringDoc OpenAPI 3 |
| Build | Maven 3.9 |
| Boilerplate | Lombok |
All configuration is externalized to git-config-repo/ and served by the config server. No service needs a local application.properties.
| Variable | Required | Description |
|---|---|---|
MYSQL_HOST |
✅ | MySQL hostname |
MYSQL_PORT |
✅ | MySQL port |
MYSQL_USER |
✅ | MySQL username |
MYSQL_PASSWORD |
✅ | MySQL password |
JWT_SECRET |
✅ | HMAC signing key (min 32 chars) |
JWT_EXPIRATION |
✅ | Token TTL in ms — e.g. 3600000 = 1 hour |
INTERNAL_TOKEN |
✅ | Shared secret for service-to-service auth |
CONFIG_SERVER_URI |
— | Default: http://localhost:8888 |
EUREKA_URI |
— | Default: http://localhost:8761/eureka |
ZIPKIN_URI |
— | Default: http://localhost:9411 |
Every request is automatically instrumented. Trace IDs propagate across service boundaries and are exported to Zipkin:
[user-service] traceId: abc123 ──► [cart-service] traceId: abc123
└──► all spans visible at :9411
# Service health
curl http://localhost:8003/actuator/health
# Circuit breaker states
curl http://localhost:8003/actuator/circuitbreakers
# Circuit breaker event log
curl http://localhost:8003/actuator/circuitbreakereventsThis project is licensed under the MIT License. See LICENSE for details.