Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
cfb4b9e
fix: align JWT secret decoding to hex in auth-service to match api-ga…
zexxitywave Sep 1, 2026
6a5ca9e
fix: release reserved inventory on PaymentFailedEvent and ShipmentFai…
zexxitywave Sep 1, 2026
1090847
fix: replace hardcoded Docker hostnames in order-service application.yml
zexxitywave Sep 1, 2026
83b6840
feat: add multi-stage Dockerfiles for all 16 services
zexxitywave Sep 1, 2026
9d8b928
feat: extend CI/CD to build and push Docker images to GHCR
zexxitywave Sep 1, 2026
95d4bd0
feat: add OpenAPI/Swagger (springdoc-openapi 2.5.0) to all services
zexxitywave Sep 1, 2026
dad6bdf
feat: implement Outbox pattern for reliable Kafka event publishing in…
zexxitywave Sep 1, 2026
b54b1df
feat: add Redis-backed JWT access token blacklist for immediate logout
zexxitywave Sep 1, 2026
d8f573e
feat: add Kafka consumer health indicator to all Kafka-enabled services
zexxitywave Sep 1, 2026
91cedbc
feat: add Kafka consumer lag monitoring and Alertmanager
zexxitywave Sep 1, 2026
a000d8a
feat: add structured HTTP access logging and role-based auth at gateway
zexxitywave Sep 1, 2026
6b50280
feat: implement Event Sourcing for order state transitions
zexxitywave Sep 1, 2026
2e86c82
feat: add SSE real-time order status stream endpoint
zexxitywave Sep 1, 2026
6e9d738
feat: replace choreography with Spring State Machine saga orchestrator
zexxitywave Sep 1, 2026
72359fe
feat: add /api/v1/ versioning to all service controllers and gateway …
zexxitywave Sep 1, 2026
b4f0aba
chore: misc service improvements and config fixes
zexxitywave Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
**/.git
**/.idea
**/*.iml
**/target
!*/target/*.jar
**/node_modules
**/.env
**/run-logs
**/load-tests
**/k8s
137 changes: 133 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
name: CI — Build & Verify
name: CI — Build, Test & Publish

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}

jobs:
build:
name: Build all modules (Java 21 + Maven)
# ── Job 1: compile + test (runs on every push/PR) ─────────────────────────
build-and-test:
name: Build & Test (Java 21 + Maven)
runs-on: ubuntu-latest

steps:
Expand All @@ -22,7 +27,7 @@ jobs:
distribution: 'temurin'
cache: maven

- name: Build all modules
- name: Build all modules (skip tests)
run: mvn clean package -DskipTests --no-transfer-progress

- name: Run unit tests
Expand All @@ -37,3 +42,127 @@ jobs:
*/target/*.jar
!*/target/*-sources.jar
retention-days: 7

# ── Job 2: build + push Docker images (main branch only) ──────────────────
docker-publish:
name: Build & Push Docker Images
runs-on: ubuntu-latest
needs: build-and-test
# Only run on pushes to main (not PRs) to avoid publishing untested images
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

permissions:
contents: read
packages: write

strategy:
# Build all services in parallel — fail-fast: false so one failure
# doesn't cancel all other service builds
fail-fast: false
matrix:
service:
- name: service-registry
port: 8761
- name: api-gateway
port: 8080
- name: auth-service
port: 8086
- name: order-service
port: 8081
- name: inventory-service
port: 8082
- name: payment-service
port: 8083
- name: notification-service
port: 8084
- name: shipping-service
port: 8085
- name: user-service
port: 8087
- name: product-service
port: 8088
- name: cart-service
port: 8089
- name: wishlist-service
port: 8090
- name: seller-service
port: 8091
- name: logging-service
port: 8092
- name: analytics-service
port: 8093

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata (tags + labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_PREFIX }}/${{ matrix.service.name }}
tags: |
# Tag with short SHA so every commit is traceable
type=sha,prefix=sha-
# Tag latest on main
type=raw,value=latest,enable={{is_default_branch}}
# Semantic version tags when a git tag is pushed (e.g. v1.2.3)
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push ${{ matrix.service.name }}
uses: docker/build-push-action@v5
with:
context: .
file: ./${{ matrix.service.name }}/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# BuildKit inline cache — reuse layers from previous builds
cache-from: type=registry,ref=${{ env.IMAGE_PREFIX }}/${{ matrix.service.name }}:buildcache
cache-to: type=registry,ref=${{ env.IMAGE_PREFIX }}/${{ matrix.service.name }}:buildcache,mode=max
build-args: |
BUILD_DATE=${{ github.event.head_commit.timestamp }}
GIT_COMMIT=${{ github.sha }}

# ── Job 3: security scan (main branch only) ───────────────────────────────
security-scan:
name: Security Scan (Trivy)
runs-on: ubuntu-latest
needs: docker-publish
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

strategy:
fail-fast: false
matrix:
service:
- order-service
- auth-service
- payment-service
- api-gateway

steps:
- name: Run Trivy vulnerability scan on ${{ matrix.service }}
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:latest
format: sarif
output: trivy-${{ matrix.service }}.sarif
severity: CRITICAL,HIGH
exit-code: '0' # Don't fail the build, just report

- name: Upload Trivy SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-${{ matrix.service }}.sarif
category: trivy-${{ matrix.service }}
24 changes: 12 additions & 12 deletions SagaFlow.postman_collection.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
{
"info": {
"name": "EventDrivenMesh - Order Saga Flow",
"description": "Clean 11-step end-to-end Order Saga flow. Run via Collection Runner in order.",
Expand Down Expand Up @@ -30,7 +30,7 @@
"request": {
"method": "POST",
"header": [{ "key": "Content-Type", "value": "application/json" }],
"url": { "raw": "{{baseUrl}}/api/auth/login", "host": ["{{baseUrl}}"], "path": ["api","auth","login"] },
"url": { "raw": "{{baseUrl}}/api/auth/login", "host": ["{{baseUrl}}"], "path": ["api","v1","auth","login"] },
"body": { "mode": "raw", "raw": "{\n \"email\": \"testuser@example.com\",\n \"password\": \"Password@123\"\n}" }
}
},
Expand All @@ -48,7 +48,7 @@
{ "key": "Content-Type", "value": "application/json" },
{ "key": "Authorization", "value": "Bearer {{accessToken}}" }
],
"url": { "raw": "{{baseUrl}}/api/products/categories", "host": ["{{baseUrl}}"], "path": ["api","products","categories"] },
"url": { "raw": "{{baseUrl}}/api/products/categories", "host": ["{{baseUrl}}"], "path": ["api","v1","products","categories"] },
"body": { "mode": "raw", "raw": "{\n \"name\": \"Smartphones\",\n \"description\": \"Mobile phones and smartphones\"\n}" }
}
},
Expand All @@ -67,7 +67,7 @@
{ "key": "Authorization", "value": "Bearer {{accessToken}}" },
{ "key": "X-User-Id", "value": "{{userId}}" }
],
"url": { "raw": "{{baseUrl}}/api/products", "host": ["{{baseUrl}}"], "path": ["api","products"] },
"url": { "raw": "{{baseUrl}}/api/products", "host": ["{{baseUrl}}"], "path": ["api","v1","products"] },
"body": { "mode": "raw", "raw": "{\n \"name\": \"Samsung Galaxy S25\",\n \"description\": \"Latest flagship smartphone\",\n \"price\": 79999.00,\n \"sku\": \"SGS25-BLK-256\",\n \"brand\": \"Samsung\",\n \"categoryId\": \"{{categoryId}}\",\n \"stockQuantity\": 100\n}" }
}
},
Expand All @@ -85,7 +85,7 @@
{ "key": "Content-Type", "value": "application/json" },
{ "key": "Authorization", "value": "Bearer {{accessToken}}" }
],
"url": { "raw": "{{baseUrl}}/api/inventory", "host": ["{{baseUrl}}"], "path": ["api","inventory"] },
"url": { "raw": "{{baseUrl}}/api/inventory", "host": ["{{baseUrl}}"], "path": ["api","v1","inventory"] },
"body": { "mode": "raw", "raw": "{\n \"productId\": \"{{productId}}\",\n \"quantity\": 100,\n \"lowStockThreshold\": 10,\n \"warehouseLocation\": \"Warehouse-A\"\n}" }
}
},
Expand All @@ -108,7 +108,7 @@
{ "key": "Content-Type", "value": "application/json" },
{ "key": "Authorization", "value": "Bearer {{accessToken}}" }
],
"url": { "raw": "{{baseUrl}}/api/orders", "host": ["{{baseUrl}}"], "path": ["api","orders"] },
"url": { "raw": "{{baseUrl}}/api/orders", "host": ["{{baseUrl}}"], "path": ["api","v1","orders"] },
"body": { "mode": "raw", "raw": "{\n \"customerId\": \"{{userId}}\",\n \"items\": [\n {\n \"productId\": \"{{productId}}\",\n \"productName\": \"Samsung Galaxy S25\",\n \"quantity\": 1,\n \"price\": 79999.00\n }\n ],\n \"shippingAddress\": {\n \"street\": \"123 MG Road\",\n \"city\": \"Bangalore\",\n \"state\": \"Karnataka\",\n \"pincode\": \"560001\",\n \"country\": \"India\"\n },\n \"totalAmount\": 79999.00\n}" }
}
},
Expand All @@ -123,7 +123,7 @@
"request": {
"method": "GET",
"header": [{ "key": "Authorization", "value": "Bearer {{accessToken}}" }],
"url": { "raw": "{{baseUrl}}/api/orders/{{orderId}}", "host": ["{{baseUrl}}"], "path": ["api","orders","{{orderId}}"] }
"url": { "raw": "{{baseUrl}}/api/orders/{{orderId}}", "host": ["{{baseUrl}}"], "path": ["api","v1","orders","{{orderId}}"] }
}
},
{
Expand All @@ -142,7 +142,7 @@
{ "key": "Content-Type", "value": "application/json" },
{ "key": "Authorization", "value": "Bearer {{accessToken}}" }
],
"url": { "raw": "{{baseUrl}}/api/payments/initiate", "host": ["{{baseUrl}}"], "path": ["api","payments","initiate"] },
"url": { "raw": "{{baseUrl}}/api/payments/initiate", "host": ["{{baseUrl}}"], "path": ["api","v1","payments","initiate"] },
"body": { "mode": "raw", "raw": "{\n \"orderId\": \"{{orderId}}\",\n \"customerId\": \"{{userId}}\",\n \"amount\": 79999.00,\n \"currency\": \"INR\",\n \"gateway\": \"MOCK\"\n}" }
}
},
Expand All @@ -157,7 +157,7 @@
"request": {
"method": "GET",
"header": [{ "key": "Authorization", "value": "Bearer {{accessToken}}" }],
"url": { "raw": "{{baseUrl}}/api/inventory/product/{{productId}}", "host": ["{{baseUrl}}"], "path": ["api","inventory","product","{{productId}}"] }
"url": { "raw": "{{baseUrl}}/api/inventory/product/{{productId}}", "host": ["{{baseUrl}}"], "path": ["api","v1","inventory","product","{{productId}}"] }
}
},
{
Expand All @@ -173,7 +173,7 @@
"request": {
"method": "GET",
"header": [{ "key": "Authorization", "value": "Bearer {{accessToken}}" }],
"url": { "raw": "{{baseUrl}}/api/shipping/order/{{orderId}}", "host": ["{{baseUrl}}"], "path": ["api","shipping","order","{{orderId}}"] }
"url": { "raw": "{{baseUrl}}/api/shipping/order/{{orderId}}", "host": ["{{baseUrl}}"], "path": ["api","v1","shipping","order","{{orderId}}"] }
}
},
{
Expand All @@ -191,7 +191,7 @@
{ "key": "Authorization", "value": "Bearer {{accessToken}}" },
{ "key": "X-User-Id", "value": "{{userId}}" }
],
"url": { "raw": "{{baseUrl}}/api/notifications", "host": ["{{baseUrl}}"], "path": ["api","notifications"] }
"url": { "raw": "{{baseUrl}}/api/notifications", "host": ["{{baseUrl}}"], "path": ["api","v1","notifications"] }
}
},
{
Expand All @@ -205,7 +205,7 @@
"request": {
"method": "GET",
"header": [{ "key": "Authorization", "value": "Bearer {{accessToken}}" }],
"url": { "raw": "{{baseUrl}}/api/notifications/invoice/{{orderId}}", "host": ["{{baseUrl}}"], "path": ["api","notifications","invoice","{{orderId}}"] }
"url": { "raw": "{{baseUrl}}/api/notifications/invoice/{{orderId}}", "host": ["{{baseUrl}}"], "path": ["api","v1","notifications","invoice","{{orderId}}"] }
}
}
]
Expand Down
29 changes: 29 additions & 0 deletions analytics-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -- Stage 1: Build ----------------------------------------------------------
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build

COPY pom.xml .
COPY common-library/pom.xml common-library/
COPY common-library/src common-library/src
COPY analytics-service/pom.xml analytics-service/
COPY analytics-service/src analytics-service/src

RUN --mount=type=cache,target=/root/.m2 `
./mvnw -pl common-library,analytics-service -am clean package -DskipTests --no-transfer-progress

# -- Stage 2: Runtime --------------------------------------------------------
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

COPY --from=builder /build/analytics-service/target/*.jar app.jar

EXPOSE 8093

ENTRYPOINT ["java", `
"-XX:+UseContainerSupport", `
"-XX:MaxRAMPercentage=75.0", `
"-Djava.security.egd=file:/dev/./urandom", `
"-jar", "app.jar"]
12 changes: 5 additions & 7 deletions analytics-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,12 @@
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- OpenAPI / Swagger UI -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,15 @@ private void sendToDlq(ConsumerRecord<String, byte[]> record, String reason) {
}
}
}

//POST /api/orders
// ↓
//OrderController
// ↓
//OrderService
// ↓
//OrderRepository
// ↓
//PostgreSQL
// ↓
//Kafka Producer
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.hacisimsek.analytics.controller;
package com.hacisimsek.analytics.controller;

import com.hacisimsek.analytics.model.OrderAnalytics;
import com.hacisimsek.analytics.repository.OrderAnalyticsRepository;
Expand All @@ -14,7 +14,7 @@
import java.util.Map;

@RestController
@RequestMapping("/api/analytics")
@RequestMapping("/api/v1/analytics")
@RequiredArgsConstructor
public class AnalyticsController {

Expand Down
29 changes: 29 additions & 0 deletions api-gateway/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -- Stage 1: Build ----------------------------------------------------------
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build

COPY pom.xml .
COPY common-library/pom.xml common-library/
COPY common-library/src common-library/src
COPY api-gateway/pom.xml api-gateway/
COPY api-gateway/src api-gateway/src

RUN --mount=type=cache,target=/root/.m2 `
./mvnw -pl common-library,api-gateway -am clean package -DskipTests --no-transfer-progress

# -- Stage 2: Runtime --------------------------------------------------------
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

COPY --from=builder /build/api-gateway/target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", `
"-XX:+UseContainerSupport", `
"-XX:MaxRAMPercentage=75.0", `
"-Djava.security.egd=file:/dev/./urandom", `
"-jar", "app.jar"]
Loading
Loading