Case study backend with Java 21 + Spring Boot 3 and PostgreSQL, split into three microservices:
- user-service – user CRUD, search, and listing a user’s organizations
- organization_service – organization CRUD, search, and membership
- invitation_service – invitations to organizations (accept/reject/expire)
Each service exposes OpenAPI/Swagger, /healtz health endpoint, auditing headers, input validation, pagination, and indexes on searchable fields.
- Tech: Java 21, Spring Boot 3, Spring Data JPA (Hibernate), Spring Validation, OpenFeign, Springdoc OpenAPI, Quartz.
- DB: PostgreSQL (one DB per service is the intended model; for local dev you may point all to one instance with different schemas).
- Communication: REST between services; Feign is used where one service needs another.
- Scheduling: Quartz job in
invitation_serviceexpires pending invitations older than 7 days.
- JDK 21
- Maven 3.9+
- PostgreSQL 14+ (or use Docker)
- Optional: Docker/Docker Compose
Each service uses standard Spring Boot properties (spring.datasource.*).
For local development, default ports are:
| Service | App Port | Swagger URL |
|---|---|---|
| invitation_service | 8080 | http://localhost:8080/swagger-ui/index.html |
| user-service | 8081 | http://localhost:8081/swagger-ui/index.html |
| organization_service | 8082 | http://localhost:8082/swagger-ui/index.html |
invitation_service (8080) – /api/invitations
POST /api/invitations – Create an invitation.
POST /api/invitations/{id}/accept – Accept (requires X-Acting-User = invitee).
POST /api/invitations/{id}/reject – Reject (requires X-Acting-User).
GET /api/invitations – List invitations: filter by orgId and/or userId, pageable.
GET /api/invitations/{id} – Get by id.
GET /healtz – Health.
Notes:
Quartz job runs daily (03:00 Europe/Istanbul) and sets old PENDING → EXPIRED.
Accept endpoint additionally defensively checks age at runtime.
user-service (8081) – /api/users
POST /api/users – Create user.
GET /api/users/search?q=&page=&size= – Search by normalizedName.
GET /api/users/email/{email} – Get by email.
GET /api/users/{id} – Get by id.
PUT /api/users/{email} – Update by email.
DELETE /api/users/{email} – Delete by email.
GET /api/users/{userId}/organizations?page=&size=&sort= – List organizations user belongs to (cross-service).
GET /healtz – Health.
organization_service (8082) – /api/organizations
POST /api/organizations – Create organization.
PUT /api/organizations/{registryNumber} – Update by registry number.
GET /api/organizations/{id} – Get by id.
GET /api/organizations/registryNumber/{registryNumber} – Get by registry number.
GET /api/organizations/search?... – Search by normalized name, size, year (paged).
POST /api/organizations/{orgId}/members?userId= – Add member (used by invitation accept).
GET /api/organizations/members/{userId}?page=&size=&sort= – List orgs a user belongs to.
GET /healtz – Health.
