Skip to content

Commit f25b6f9

Browse files
authored
Merge pull request #1 from jawherr/feature/1-init-structure-implementation
Initial implementation structure of the main microservice java project.
2 parents 8f4d812 + 1f69532 commit f25b6f9

17 files changed

Lines changed: 537 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
java: ["17"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
- name: Set up JDK ${{ matrix.java }}
19+
uses: actions/setup-java@v4
20+
with:
21+
distribution: temurin
22+
java-version: ${{ matrix.java }}
23+
cache: maven
24+
25+
- name: Build and run tests
26+
run: mvn -B -DskipTests=false clean verify
27+
28+
- name: Build app Docker image
29+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
30+
run: |
31+
docker build -t java-microservices-app:latest ./app
32+

.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Maven
2+
**/target/
3+
**/release/
4+
5+
# Eclipse / IntelliJ
6+
.classpath
7+
.project
8+
.settings/
9+
*.iml
10+
.idea/
11+
12+
# OS files
13+
.DS_Store
14+
Thumbs.db
15+
16+
# Logs
17+
*.log
18+
19+
# Maven wrapper
20+
.mvn/wrapper/maven-wrapper.jar
21+
22+
# local maven repo
23+
.m2/
24+
25+
# Docker
26+
docker-compose.override.yml
27+
28+
# VS Code
29+
.vscode/
30+

README.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,120 @@
11
# Java Microservices
2+
3+
This repository is a blueprint and starter kit for building high-performance, production-ready Java backend systems that can be implemented either as a performant monolith or as a scalable set of microservices.
4+
5+
## Goals
6+
- Provide a pragmatic, senior-backend-developer-approved architecture and tooling selection.
7+
- Show recommended technology choices (Spring Boot + Quarkus) and cross-cutting tools for DB, messaging, caching, CI/CD, observability and security.
8+
- Offer an incremental path: start with a modular high-performance monolith and split to microservices where it makes sense.
9+
10+
## High-level vision
11+
- Keep core business logic domain-driven and framework-agnostic.
12+
- Design for observability, security, and automation from day one.
13+
- Use modern, proven tools to enable fast developer feedback and safe production deployments.
14+
15+
## Key non-functional requirements
16+
- Performance: low latency (sub-100ms P95 for typical API calls), high throughput.
17+
- Scalability: horizontal scalability for stateless services, and appropriate patterns for stateful services.
18+
- Reliability: graceful degradation, retries, bulkheads, and circuit breakers.
19+
- Maintainability: small modules/contexts, strong typing, and automated tests.
20+
- Observability: structured logs, distributed tracing, metrics, and alerting.
21+
22+
## Recommended tech stack
23+
- JVM & language: Java 17 or 21 (LTS), use modern language features where helpful.
24+
- Frameworks:
25+
- Spring Boot 3.x for full-featured, ecosystem-rich services (REST, Spring Data, Spring Security).
26+
- Quarkus for performance-sensitive services (fast startup, low memory) and native compilation where needed.
27+
- Build tools: Maven or Gradle (pick one consistently). Provide sample Maven setup by default.
28+
- Database: PostgreSQL (primary OLTP store). Use R2DBC for reactive services where appropriate; otherwise use JDBC via Spring Data JPA.
29+
- Migrations: Flyway (or Liquibase) for database schema/version control.
30+
- Caching: Redis (for distributed caches) or Hazelcast (in-memory grid) when needed.
31+
- Messaging / events: Apache Kafka (event-driven, durable streaming) and RabbitMQ (if you need simpler broker semantics).
32+
- API protocols: REST (OpenAPI), gRPC for high-performance polyglot RPC, GraphQL optional for rich client queries.
33+
- Security: OAuth 2.0 / OIDC (Keycloak or Auth0), JWT tokens, Spring Security, and Vault for secrets.
34+
- Observability: OpenTelemetry (traces + metrics), Jaeger/Tempo for tracing, Prometheus for metrics, Grafana for dashboards.
35+
- Testing: JUnit 5, Mockito, Testcontainers (for integration tests using real Postgres/Kafka), Pact for contract testing.
36+
- Performance testing: Gatling or k6 for load tests; JMH for microbenchmarks.
37+
- Containerization / orchestration: Docker, Kubernetes (K8s), Helm charts; optionally use Kustomize or ArgoCD for GitOps.
38+
- CI/CD: GitHub Actions or GitLab CI for pipelines, with stages for build, test, security scans, container publish, and deploy.
39+
- Secrets & Config: HashiCorp Vault for secrets, Spring Cloud Config / Consul or environment-driven 12-factor config for app configuration.
40+
41+
## Architecture patterns & design principles
42+
- Start as a modular monolith (multi-module Gradle/Maven project) organized by bounded contexts. This gives fast developer feedback and avoids premature distributed systems complexity.
43+
- Apply Hexagonal / Ports & Adapters architecture to keep business logic independent from frameworks and infrastructure.
44+
- Use Domain-Driven Design (DDD) to identify bounded contexts and where to split into microservices.
45+
- Prefer asynchronous messaging and event-driven integration for inter-service communication when eventual consistency is acceptable.
46+
- Use API Gateway for external APIs (rate-limiting, authentication, routing). Keep internal APIs lightweight.
47+
- Use health checks (liveness/readiness), graceful shutdown, and resource limits for containers.
48+
49+
## Project layout suggestions
50+
- Monolith (modular):
51+
- /app (service application starters) - Spring Boot / Quarkus launchers
52+
- /domain (shared domain model & services)
53+
- /infrastructure (db, messaging, cache adapters)
54+
- /api (REST controllers / gRPC endpoints)
55+
- /integration-tests (Testcontainers-based tests)
56+
- Microservices:
57+
- service-name/ (each service: own module/repo, own Dockerfile, Helm chart)
58+
- shared-libs/ (common libraries maintained with versioning)
59+
60+
## Implementation contract (short)
61+
- Inputs: HTTP/gRPC requests, async messages, DB events.
62+
- Outputs: HTTP/gRPC responses, domain events, DB writes, metrics, logs, traces.
63+
- Error modes: transient infra failures (handled by retries & backoff), validation errors (client 4xx), auth/permission (401/403).
64+
- Success criteria: automated build + tests, deployable container image, passing health checks, and basic load test within target SLOs.
65+
66+
## Developer UX & local dev setup
67+
- Provide a docker-compose file to bring up Postgres, Kafka (or RabbitMQ), Redis, and Keycloak for local development.
68+
- Use dev profiles (Spring profiles or Quarkus config) to switch between in-memory/mocked dependencies and real infra.
69+
- Prefer devtools / hot-reload (Spring DevTools, Quarkus dev mode) for fast feedback.
70+
71+
## CI/CD pipeline outline
72+
1. Checkout code and run static analysis (spotbugs, checkstyle, dependency-check).
73+
2. Build with Maven/Gradle and run unit tests.
74+
3. Run integration tests using Testcontainers (or a test environment).
75+
4. Build Docker image and run a lightweight smoke test.
76+
5. Push image to registry and create an immutable version/tag.
77+
6. Deploy to staging via Helm or GitOps, run end-to-end and contract tests.
78+
7. Promote to production with a controlled rollout (canary, blue/green).
79+
80+
## Security checklist
81+
- Enforce HTTPS everywhere (nginx/ALB + service TLS).
82+
- Use OAuth2/OIDC for authentication; never roll your own auth.
83+
- Validate and sanitize inputs; use parameterized queries or ORM to prevent SQL injection.
84+
- Short-lived JWTs + refresh tokens; rotate secrets stored in Vault.
85+
- Apply principle of least privilege for service accounts and database credentials.
86+
- Use static analysis and dependency vulnerability scanning (Snyk, Dependabot, or OWASP Dependency-Check) in the pipeline.
87+
88+
## Observability & SLOs
89+
- Capture structured logs (JSON) with request IDs.
90+
- Export traces and metrics via OpenTelemetry libraries to Jaeger and Prometheus.
91+
- Define SLOs (error rates, latency P95/P99) and set up Grafana dashboards and alerts.
92+
93+
## Testing strategy
94+
- Unit tests for business logic (JUnit 5 + Mockito), aim for fast execution.
95+
- Integration tests using Testcontainers to run Postgres/Kafka in CI for realistic integration.
96+
- Contract tests (Consumer-Driven Contracts) to protect service boundaries.
97+
- End-to-end smoke tests after deployment to staging.
98+
- Load testing in staging with real-ish data using Gatling/k6.
99+
100+
## Performance tips
101+
- Prefer connection pooling (HikariCP), efficient query patterns, and proper indexing for Postgres.
102+
- For high concurrency paths, consider reactive stacks (R2DBC, WebFlux, Mutiny in Quarkus) and benchmark carefully.
103+
- Use caching for read-heavy endpoints; measure cache hit ratio and TTLs.
104+
- Profile hot paths using async-profiler / JFR and iterate.
105+
106+
## Minimal MVP (first milestone)
107+
1. Core domain module with a single bounded context (e.g., Orders, Users, Inventory) implemented in a modular monolith.
108+
2. REST API with OpenAPI docs and basic CRUD flows.
109+
3. Postgres persistence with Flyway-managed schema.
110+
4. Dockerfile and docker-compose for local dev (Postgres + Redis + Keycloak minimal).
111+
5. CI pipeline with build, unit tests and a basic integration stage.
112+
6. Logging, health endpoints, and a Prometheus metrics endpoint.
113+
114+
## Roadmap & next steps
115+
- Phase 1: Create modular monolith with domain-first design + baseline CI and infra (DB, cache).
116+
- Phase 2: Add observability (traces, metrics, dashboards) and security (Keycloak integration).
117+
- Phase 3: Introduce asynchronous messaging and eventing for selected flows.
118+
- Phase 4: Split off the first microservice from monolith (bounded context extraction) and deploy to Kubernetes.
119+
- Phase 5: Harden CI/CD, rollout strategies, and secrets management.
120+

app/Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Multi-stage Dockerfile
2+
FROM maven:3.9.4-eclipse-temurin-17 AS build
3+
WORKDIR /workspace
4+
COPY . /workspace
5+
RUN mvn -B -DskipTests package -pl app -am
6+
7+
FROM eclipse-temurin:17-jre-alpine
8+
WORKDIR /app
9+
COPY --from=build /workspace/app/target/*.jar /app/app.jar
10+
EXPOSE 8080
11+
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
12+

app/pom.xml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>com.example</groupId>
8+
<artifactId>java-microservices</artifactId>
9+
<version>0.0.1-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>app</artifactId>
13+
<packaging>jar</packaging>
14+
15+
<name>app</name>
16+
17+
<properties>
18+
<spring-boot.version>${spring-boot.version}</spring-boot.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-web</artifactId>
25+
</dependency>
26+
27+
<dependency>
28+
<groupId>com.example</groupId>
29+
<artifactId>core</artifactId>
30+
<version>0.0.1-SNAPSHOT</version>
31+
</dependency>
32+
33+
<dependency>
34+
<groupId>com.example</groupId>
35+
<artifactId>common</artifactId>
36+
<version>0.0.1-SNAPSHOT</version>
37+
</dependency>
38+
39+
<dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-starter-test</artifactId>
42+
<scope>test</scope>
43+
</dependency>
44+
</dependencies>
45+
46+
<build>
47+
<plugins>
48+
<plugin>
49+
<groupId>org.springframework.boot</groupId>
50+
<artifactId>spring-boot-maven-plugin</artifactId>
51+
</plugin>
52+
</plugins>
53+
</build>
54+
</project>
55+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.example.app;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication(scanBasePackages = "com.example")
7+
public class Application {
8+
public static void main(String[] args) {
9+
SpringApplication.run(Application.class, args);
10+
}
11+
}
12+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.example.app.controller;
2+
3+
import com.example.common.dto.GreetingDto;
4+
import com.example.core.service.GreetingService;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.RequestParam;
7+
import org.springframework.web.bind.annotation.RestController;
8+
9+
@RestController
10+
public class GreetingController {
11+
12+
private final GreetingService greetingService;
13+
14+
public GreetingController(GreetingService greetingService) {
15+
this.greetingService = greetingService;
16+
}
17+
18+
@GetMapping("/api/greet")
19+
public GreetingDto greet(@RequestParam(required = false) String name) {
20+
String message = greetingService.greet(name);
21+
return new GreetingDto(name, message);
22+
}
23+
}
24+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
server:
2+
port: 8080
3+
4+
spring:
5+
main:
6+
allow-bean-definition-overriding: false
7+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.example.app;
2+
3+
import com.example.common.dto.GreetingDto;
4+
import org.junit.jupiter.api.Test;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
import org.springframework.boot.test.web.client.TestRestTemplate;
8+
import org.springframework.boot.web.server.LocalServerPort;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
12+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
13+
public class GreetingControllerTest {
14+
15+
@LocalServerPort
16+
private int port;
17+
18+
@Autowired
19+
private TestRestTemplate restTemplate;
20+
21+
@Test
22+
void greetEndpoint() {
23+
GreetingDto resp = this.restTemplate.getForObject("http://localhost:" + port + "/api/greet?name=Bob", GreetingDto.class);
24+
assertThat(resp).isNotNull();
25+
assertThat(resp.getMessage()).isEqualTo("Hello, Bob");
26+
}
27+
}
28+

common/pom.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>com.example</groupId>
8+
<artifactId>java-microservices</artifactId>
9+
<version>0.0.1-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>common</artifactId>
13+
<packaging>jar</packaging>
14+
15+
<name>common</name>
16+
17+
<dependencies>
18+
<!-- Jackson is available via Spring Boot in app, but add a small dependency if standalone testing is desired -->
19+
<dependency>
20+
<groupId>com.fasterxml.jackson.core</groupId>
21+
<artifactId>jackson-databind</artifactId>
22+
<version>2.15.2</version>
23+
<scope>compile</scope>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.junit.jupiter</groupId>
27+
<artifactId>junit-jupiter</artifactId>
28+
<version>5.10.0</version>
29+
<scope>test</scope>
30+
</dependency>
31+
</dependencies>
32+
</project>
33+

0 commit comments

Comments
 (0)