Skip to content

Repository files navigation

Spring Boot Microservices Platform

Local-first microservices platform with 4 domain services (user, order, payment, notification) plus React frontend. Single GraphQL entry plus gRPC, Consul discovery, Kafka events, Resilience4j, PostgreSQL per service, Jaeger tracing with OTel, 4 relationship types.

Prerequisites

  • JDK 17 (java -version must report 17.x, set JAVA_HOME=$HOME/jdk17 or /usr/lib/jvm/java-17-openjdk-amd64 if needed, never lower project Java version)
  • Docker and Docker Compose (tested with Docker 24+ and Compose v2)
  • Maven wrapper (./mvnw) per service, or make build
  • Linters: Java uses Spotless (google-java-format 1.17) + Checkstyle 10.18 + SpotBugs 4.8 + PMD 7.3, frontend uses ESLint 8 + Prettier 3
  • Optional: grpcurl for gRPC smoke, jq for JSON
java -version
javac -version
docker --version
docker compose version

One Command Start

From clean clone:

git clone <repo> && cd springboot-dev
make start
make ps
make logs

make start does docker compose up --build -d for all infra plus services with healthchecks and depends.

Ports

Component HTTP gRPC Host Exposed Notes
api-gateway 8080 6560 yes (gateway) Single entry for GraphQL and gRPC
user-service 8081 6565 dev only GraphQL at /graphql
order-service 8082 6566 dev only GraphQL at /graphql
payment-service 8083 6567 dev only GraphQL at /graphql
notification-service 8084 6568 dev only GraphQL at /graphql, streaming
consul 8500 8600 no (internal) UI at http://localhost:8500
kafka 9092 29092 internal no
zookeeper 2181 - no
postgres-user 5432 - dev only userdb
postgres-order 5433 - dev only orderdb
postgres-payment 5434 - dev only paymentdb
postgres-notification 5435 - dev only notificationdb
jaeger 16686 14250, 4317 yes 16686 UI at http://localhost:16686
otel-collector - 4317/4318 yes 4317 OTLP endpoint http://otel-collector:4317
frontend 5173 - yes Vite dev or Nginx prod

In production, only 8080, 6560, 16686, 5173 are host exposed. Services keep ports internal only via platform-net. For local dev, services expose 8081-8084 and 6565-6568 for debugging via profile application.yml vs docker compose. Use docker-compose.prod.yml overlay that removes service ports if needed.

Architecture Diagram

graph TB
  frontend[Frontend 5173 React Vite] --> gateway[API Gateway 8080/6560<br/>Spring Cloud Gateway + gRPC]
  gateway -->|lb://| user[User Service 8081/6565<br/>GraphQL + gRPC]
  gateway -->|lb://| order[Order Service 8082/6566<br/>GraphQL + gRPC]
  gateway -->|lb://| payment[Payment Service 8083/6567<br/>GraphQL + gRPC]
  gateway -->|lb://| notif[Notification Service 8084/6568<br/>GraphQL + gRPC streaming]
  user -->|gRPC validate| order
  order -->|gRPC validate| user
  payment -->|gRPC fetch order| order
  user -->|publish UserCreated| kafka[Kafka 9092<br/>user.events order.events payment.events]
  order -->|publish OrderCreated| kafka
  payment -->|publish PaymentCompleted| kafka
  kafka --> notif
  user --> pgU[(Postgres userdb 5432)]
  order --> pgO[(Postgres orderdb 5433)]
  payment --> pgP[(Postgres paymentdb 5434)]
  notif --> pgN[(Postgres notificationdb 5435)]
  gateway -.->|discovery| consul[Consul 8500]
  user -.-> consul
  order -.-> consul
  payment -.-> consul
  notif -.-> consul
  gateway -->|OTLP 4317| otel[OTel Collector 4317]
  user -->|OTLP| otel
  order -->|OTLP| otel
  payment -->|OTLP| otel
  notif -->|OTLP| otel
  otel --> jaeger[Jaeger 16686]
Loading

Service List

  • api-gateway: Spring Cloud Gateway, GraphQL aggregation, gRPC proxy on 6560, Resilience4j circuit breaker per route with fallback /fallback/*, RateLimiter and Bulkhead, correlation X-Request-Id, Jaeger W3C, CORS for 5173
  • user-service: JPA entities User, UserProfile, Role, GraphQL queries user users and mutations createUser updateUser, gRPC UserService 6565, Kafka user.events, Flyway V1__init.sql
  • order-service: Order, OrderItem, Product, GraphQL order ordersByUser createOrder cancelOrder, gRPC 6566, validates user via gRPC user-service:6565 with @CircuitBreaker userService and fallback empty, Kafka order.events
  • payment-service: Payment, Transaction, GraphQL payment paymentsByOrder initiatePayment refundPayment, gRPC 6567, validates order via gRPC order-service:6566 with breaker orderService fallback empty, Kafka payment.events
  • notification-service: Notification, NotificationTemplate, UserNotification with streaming StreamNotifications, GraphQL 6568, template fetch via TemplateService with breaker templateService cached fallback, Kafka consumers for all events

DB Relationships Summary

See knowledge-base/wiki/concepts/data-model.md for full ER diagram.

  • 1:1 users 1:1 user_profiles via UNIQUE FK user_profiles.user_id -> users.id
  • N:N users N:N roles via user_roles join table
  • 1:N / N:1 orders 1:N order_items, order_items N:1 orders
  • N:N orders N:N products via order_product_tags and via order_items
  • 1:1 logical orders 1:1 payments via payments.order_id UNIQUE
  • 1:N / N:1 payments 1:N transactions
  • N:N users logical N:N notifications via user_notifications inbox
  • N:1 notifications -> notification_templates via code

Each service owns its PostgreSQL 15 DB (userdb, orderdb, paymentdb, notificationdb). Cross-service references are UUID columns without FK constraints, validated via gRPC and eventual via Kafka.

Verification

docker compose config   # lint compose
java -version           # 17.x
make lint               # Spotless, Checkstyle, SpotBugs, PMD
make lint-frontend      # ESLint + Prettier
make test               # per-service ./mvnw test
curl http://localhost:8500/v1/catalog/services
curl http://localhost:8080/actuator/gateway/routes
curl http://localhost:8081/actuator/health
curl -X POST http://localhost:8080/graphql -H "Content-Type: application/json" -d '{"query":"{ users { id } }"}'
grpcurl -plaintext localhost:6565 list
grpcurl -plaintext localhost:6565 grpc.health.v1.Health/Check
grpcurl -plaintext localhost:6560 list
docker exec kafka kafka-topics.sh --list --bootstrap-server localhost:9092
curl http://localhost:16686/api/traces?service=user-service | jq
make logs | grep traceId

Circuit breaker trip: stop user-service, create order via order-service GraphQL, verify fallback returns and breaker opens at /actuator/circuitbreakers, logs show CircuitBreaker 'userService' is OPEN, after restart breaker goes HALF_OPEN then CLOSED. Gateway fallback returns 503 with Retry-After at /fallback/*.

Jaeger: http://localhost:16686 shows trace gateway -> user-service gRPC -> order-service -> payment-service, traceId matches log pattern %X{traceId} %X{spanId} %X{requestId}.

Makefile Targets

See top of Makefile for list. Common:

  • make start - build and start all containers detached
  • make stop - stop containers
  • make restart - stop and start again
  • make logs - follow all logs, make logs-user-service for one
  • make ps - list containers
  • make clean - down with volumes and orphans
  • make build - build images
  • make test - run tests
  • make lint - run Java linters (Spotless check, Checkstyle, SpotBugs, PMD) on all services
  • make lint-fix - auto-format Java via Spotless
  • make lint-frontend - run ESLint and Prettier on frontend
  • make kafka-topics consul-ui grpc-health grpc-list jaeger-ui traces health

Troubleshooting

  • java -version not 17: install openjdk-17-jdk, set JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
  • docker compose up fails healthcheck: run docker compose ps, check docker logs consul, ensure 8500 free
  • Port already in use 8080 or 5432: lsof -i :8080 then make stop or kill process
  • grpcurl not found: install via go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
  • Kafka topics not created: check kafka-init logs, script scripts/init-topics.sh, ensure KAFKA_AUTO_CREATE_TOPICS_ENABLE=false
  • Tracing missing: check OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 env, docker logs otel-collector, Jaeger UI http://localhost:16686, logs pattern traceId
  • Circuit breaker not opening: threshold is 10 calls with 50 percent failure, make at least 5 calls, check /actuator/circuitbreakers and /actuator/health has circuitBreakers: UP/DOWN
  • Frontend blank: check http://localhost:5173 and VITE_API_URL=http://localhost:8080, CORS allows 5173, docker logs frontend
  • DB connection: check postgres-* health pg_isready, docker exec postgres-user psql -U platform -d userdb -c "\\dt"
  • Consul not showing services: wait 15s health interval, check each /actuator/health includes db, kafka, consul, grpc

Docs

  • Plans: plans/00-overview.md and plans/phases/
  • Knowledge base: knowledge-base/README.md with wiki/concepts/ (data-model, kafka-topics, graphql-federation, grpc-contracts, tracing, consul-architecture, resilience) and wiki/adrs/

Project Structure

springboot-dev/
├── api-gateway/
├── user-service/
├── order-service/
├── payment-service/
├── notification-service/
├── frontend/
├── knowledge-base/
│   ├── raw/
│   ├── wiki/
│   ├── outputs/
│   └── tools/
├── plans/
├── scripts/
├── docker-compose.yml
├── otel-collector-config.yaml
├── Makefile
└── .env.example

Exit Gate

All 6 phases green. One command git clone ... && make start boots gateway 8080 plus gRPC 6560-6568, Consul discovery 8500, Kafka events, Resilience4j on both paths, PostgreSQL per service, Jaeger tracing with OTel, 4 relationship types, React frontend, empty knowledge-base ready.

About

TBD

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages