This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Per-directory CLAUDE.md files with implementation details, gotchas, and usage instructions:
| Directory | CLAUDE.md | Description |
|---|---|---|
frontend/ |
frontend/CLAUDE.md | React 18 + TypeScript SPA: auth flow, React Query hooks, form validation, CloudFront path patterns |
services/auth-service/ |
services/auth-service/CLAUDE.md | JWT issuance, BCrypt, Spring Security FilterChain, Flyway schema |
services/application-service/ |
services/application-service/CLAUDE.md | Core CRUD, Redis dashboard cache, RabbitMQ publisher, targeting notes |
services/notification-service/ |
services/notification-service/CLAUDE.md | RabbitMQ consumer, DLQ + retry policy, notification REST API |
services/gateway-service/ |
services/gateway-service/CLAUDE.md | Spring Cloud Gateway routing, CORS, ECS Service Connect |
infra/ |
infra/CLAUDE.md | Docker Compose (local dev), environment variables, volume management |
infra/terraform/ |
infra/terraform/CLAUDE.md | AWS Terraform IaC: VPC, ECS, RDS, Redis, MQ, ALB, CloudFront, ECR, Secrets Manager |
Job Application Tracker + Resume Targeting Assistant — a hybrid-cloud full-stack microservices portfolio project. See .claude/README.md for the full feature spec, domain model, and API surface.
Do not introduce technologies outside this list without explicit justification:
| Layer | Technology |
|---|---|
| Frontend | React 18 + TypeScript, Vite, React Router v6, Axios, TanStack Query, React Hook Form + Zod, Tailwind CSS |
| Backend | Java 17, Spring Boot 3.x (jakarta.*), Spring Cloud Gateway |
| Auth | Spring Security (SecurityFilterChain bean), JWT |
| Persistence | MySQL on AWS RDS, Spring Data JPA |
| Cache | Redis on AWS ElastiCache — Application Service only |
| Messaging | RabbitMQ via Amazon MQ with Jackson2JsonMessageConverter; DLQ required on every queue |
| Containerisation | Docker (multi-stage builds); images stored in AWS ECR |
| Infrastructure (local) | Docker Compose — infra/docker-compose.yml (infra only) and infra/docker-compose.full.yml (full stack) |
| Infrastructure (cloud) | AWS ECS Fargate (services), RDS MySQL, ElastiCache Redis, Amazon MQ, ALB, CloudFront + S3 (frontend), Secrets Manager |
| IaC | Terraform (infra/terraform/) |
| CI/CD | GitHub Actions (.github/workflows/deploy.yml) — build → ECR push → ECS deploy + S3 sync → CloudFront invalidation |
| Testing | JUnit, Spring Boot Test |
- Auth Service is the sole owner of credentials and JWT issuance/validation.
- Application Service owns job applications and resume-targeting notes.
- Notification Service consumes RabbitMQ events and never calls other services directly.
- No cross-service DB joins — services never share a schema.
- Sync = REST via API Gateway. Async = RabbitMQ.
- Redis caching: Application Service only.
- Frontend calls API Gateway only — never individual services directly.
- All downstream service URIs in the gateway are env-var-driven — never hardcoded.
- Secrets (DB passwords, JWT secret, MQ password) live in AWS Secrets Manager in production; injected as ECS task env vars. Never in committed files.
Browser
│
▼
CloudFront (d3tmito2x0icp5.cloudfront.net)
│
├── /assets/*, / (default) → S3 bucket (static React SPA)
│
├── /auth/* ─┐
├── /applications* ─┤→ ALB (public subnet)
└── /notifications* ─┘ │
▼
gateway-service (ECS Fargate, private subnet)
├── /auth/** → auth-service (ECS Fargate, private subnet)
├── /applications/** → application-service (ECS Fargate, private subnet)
└── /notifications/**→ notification-service (ECS Fargate, private subnet)
Managed services (all in private subnet):
• RDS MySQL 8.0 — auth_db, application_db, notification_db
• ElastiCache Redis — application-service dashboard cache
• Amazon MQ (RabbitMQ 3.x) — async deadline events
Service discovery: ECS Service Connect (internal DNS)
Secrets: AWS Secrets Manager → ECS task env injection
Images: AWS ECR (one repo per service, mutable tags)
Frontend: S3 (private) → CloudFront OAC
CloudFront path pattern convention: Use /path* (no slash before wildcard) for any API prefix where the base path itself is a valid endpoint (e.g., GET /notifications, GET /applications). Use /path/* only when all endpoints always have a sub-path. Current behaviors: /auth/*, /applications*, /notifications*.
- Package-by-feature:
com.app.{feature} - Never expose JPA entities in responses — use DTOs (Java records preferred)
- Constructor injection;
@Transactionalon all write methods;@Validon all request DTOs - Global
@RestControllerAdvice; domain-specific exceptions; consistent error body:{ status, error, message, timestamp, path } - OpenAPI annotations on all endpoints
- RabbitMQ: exchanges/queues/bindings as
@Bean;@RabbitListenerwith DLQ; JSON serialization - Redis: explicit
RedisCacheManager; cache names as constants; document TTL and invalidation - Schema changes: always include a Flyway/Liquibase migration
- All external host/port/credential config must be env-var-driven (no defaults that point to localhost in production profiles)
- Single Axios instance at
src/lib/apiClient.tswith JWT interceptor; no rawfetch AuthContext+useAuthhook; JWT inlocalStorage;<ProtectedRoute>for gating- React Query for server state; hooks co-located in
src/features/{feature}/; invalidate keys on mutations - React Hook Form + Zod; field names must match backend DTO exactly
- No
any; explicit prop interfaces; every async UI needs loading, error, and empty states VITE_API_BASE_URLenv var drives the gateway URL. In production it is empty — CloudFront routes API paths to the ALB so relative URLs work. In local dev it is also empty and Vite proxy handles routing.- Always use
Array.isArray()before calling array methods on data fromuseQuery— unexpected non-array responses (e.g., from misconfigured routing) must not crash the app.
- MySQL:
snake_caseplural tables;id BIGINT UNSIGNED AUTO_INCREMENT; alwayscreated_at/updated_at - Redis key pattern:
{service}:{entity}:{identifier}; every key needs an explicit TTL via env var - RabbitMQ: exchanges
{domain}.{type}; queues{service}.{domain}.{event}; DLQs suffixed.dlq - Env vars:
UPPER_SNAKE_CASE, prefixed by concern (MYSQL_*,REDIS_*,RABBITMQ_*,AWS_*); never hardcoded; only.env.examplecommitted - Dockerfiles: multi-stage (Maven build → JRE runtime); use
eclipse-temurin:17-jre-alpineas runtime base - ECS task definitions:
awslogslog driver → CloudWatch; explicit CPU/memory per service - ECR repositories: MUTABLE image tags (
:latestis re-pushed on every deploy)
Every Spring Boot service has a Dockerfile at its root:
Stage 1 (builder): maven:3.9-eclipse-temurin-17-alpine — compile + package
Stage 2 (runtime): eclipse-temurin:17-jre-alpine — copy jar, EXPOSE port, ENTRYPOINT
- Non-root user (
appuser) created in the runtime stage - No secrets in Dockerfile or build args
- Health check:
HEALTHCHECK CMD wget -qO- http://localhost:{PORT}/actuator/health || exit 1
- Trigger: push to
mainorworkflow_dispatch - Five jobs run in parallel:
deploy-auth,deploy-application,deploy-notification,deploy-frontend, anddeploy-gateway(depends on all three service jobs) - Steps per backend service: checkout → set up JDK → Maven build → ECR login → Docker build+push → ECS update-service → wait stable
- Frontend job: checkout → Node 20 → npm ci → Vite build (VITE_API_BASE_URL="") → S3 sync → CloudFront invalidation
- ECS service update uses
--force-new-deploymentto trigger rolling replace - Required GitHub Secrets:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGIONECS_CLUSTER_NAME(e.g.,jat-staging-cluster)FRONTEND_S3_BUCKET(e.g.,jat-staging-frontend-217870417104)CLOUDFRONT_DISTRIBUTION_ID(e.g.,E37UWNQZYSAOAQ)
| Agent | Invoke when |
|---|---|
system-architect |
Before any new feature, service boundary decision, or API/event contract |
backend-spring |
Spring Boot controllers, services, repos, DTOs, JWT, RabbitMQ, Redis |
frontend-react |
React pages, forms, routing, API integration, auth flows |
data-infra-engineer |
Docker Compose, MySQL schema, Redis strategy, RabbitMQ topology, env vars, Dockerfiles |
aws-deploy |
AWS ECS, ECR, RDS, ElastiCache, Amazon MQ, ALB, CloudFront, Secrets Manager, Terraform, GitHub Actions CI/CD |
code-reviewer |
Before commits/PRs and after major feature integration |
docs-writer |
README updates, API docs, demo scripts, recruiter summaries |
| Command | Purpose |
|---|---|
/plan-feature |
Plan a feature across all layers before implementation |
/scaffold-service |
Bootstrap a new Spring Boot service |
/implement-endpoint |
Implement one endpoint end-to-end |
/implement-frontend-page |
Build a React page wired to a backend endpoint |
/add-cache |
Add Redis caching to an existing service method |
/add-rabbitmq-event |
Add a new RabbitMQ producer/consumer pair |
/write-tests |
Write tests for an existing implementation |
/review-pr |
Code review before opening a PR |
/update-readme |
Sync README with current implementation |
/dockerize-service |
Add or fix a Dockerfile for a Spring Boot service |
/provision-aws |
Plan and apply Terraform for AWS infrastructure |
/setup-cicd |
Add or update the GitHub Actions deployment pipeline |