Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 54 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: CI

on:
push:
branches: [dev, main, master]
pull_request:
branches: [dev, main, master]

jobs:
build:
name: Build all services
runs-on: ubuntu-latest

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

- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'

- name: Build all services
run: mvn clean package -DskipTests

- name: Upload API Gateway JAR
uses: actions/upload-artifact@v4
with:
name: api-gateway
path: api-gateway/target/api-gateway.jar
retention-days: 7

- name: Upload Order Service JAR
uses: actions/upload-artifact@v4
with:
name: order-service
path: order-service/target/order-service.jar
retention-days: 7

- name: Upload Payment Service JAR
uses: actions/upload-artifact@v4
with:
name: payment-service
path: payment-service/target/payment-service.jar
retention-days: 7

- name: Upload Notification Service JAR
uses: actions/upload-artifact@v4
with:
name: notification-service
path: notification-service/target/notification-service.jar
retention-days: 7
95 changes: 95 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Integration Tests

on:
push:
branches: [dev, main, master]
pull_request:
branches: [dev, main, master]

jobs:
integration-test:
name: Full pipeline test with RabbitMQ
runs-on: ubuntu-latest

services:
rabbitmq:
image: rabbitmq:3-management
ports:
- 5672:5672
- 15672:15672
options: >-
--health-cmd "rabbitmq-diagnostics -q ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

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

- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'

- name: Build all services
run: mvn clean package -DskipTests

- name: Start background services
run: |
java -jar order-service/target/order-service.jar > order-service.log 2>&1 &
java -jar payment-service/target/payment-service.jar > payment-service.log 2>&1 &
java -jar notification-service/target/notification-service.jar > notification-service.log 2>&1 &
java -jar api-gateway/target/api-gateway.jar > api-gateway.log 2>&1 &

- name: Wait for API Gateway to be ready
run: |
echo "Waiting for API Gateway..."
for i in $(seq 1 30); do
if curl -sf http://localhost:8080/api > /dev/null 2>&1; then
echo "API Gateway is up after ${i} attempts"
exit 0
fi
sleep 2
done
echo "API Gateway did not start in time"
cat api-gateway.log
exit 1

- name: GET /api — health check
run: |
response=$(curl -sf http://localhost:8080/api)
echo "Response: $response"
[ "$response" = "Hello World!" ]

- name: POST /api/order — full message flow
run: |
response=$(curl -s -X POST http://localhost:8080/api/order \
-H "Content-Type: application/json" \
-d '{"id":1,"product":"Widget","quantity":2,"price":19.99}')
echo "Response: $response"
echo "$response" | grep -q "Order sent to RabbitMQ"

- name: Wait for messages to propagate
run: sleep 3

- name: Verify Order Service processed the order
run: grep -q "\[Order-Service\]" order-service.log && echo "Order Service OK"

- name: Verify Payment Service processed the payment
run: grep -q "\[Payment-Service\]" payment-service.log && echo "Payment Service OK"

- name: Verify Notification Service sent both emails
run: |
grep -q "Sending Order Created Email" notification-service.log && echo "Order email OK"
grep -q "Sending Payment Succeed Email" notification-service.log && echo "Payment email OK"

- name: Print all logs on failure
if: failure()
run: |
echo "=== API Gateway ===" && cat api-gateway.log || true
echo "=== Order Service ===" && cat order-service.log || true
echo "=== Payment Service ===" && cat payment-service.log || true
echo "=== Notification Service ===" && cat notification-service.log || true
53 changes: 21 additions & 32 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
# compiled output
/dist
/node_modules
/build
# Maven build output
target/
*.jar
*.war
*.ear

# Maven wrapper download cache
.mvn/wrapper/maven-wrapper.jar

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output
Thumbs.db

# IDEs and editors
/.idea
.idea/
*.iml
.project
.classpath
.c9/
Expand All @@ -37,20 +33,13 @@ lerna-debug.log*

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# temp directory
.temp
.tmp

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
.env.*

# Spring Boot
HELP.md

# Claude Code config (local only)
.claude/

# Service runtime logs
*.log
2 changes: 2 additions & 0 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
4 changes: 0 additions & 4 deletions .prettierrc

This file was deleted.

73 changes: 73 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# ─── OS Detection ────────────────────────────────────────────────────────────
ifeq ($(OS),Windows_NT)
MVN := mvnw.cmd
KILL_JAVA := taskkill /F /IM java.exe
else
MVN := ./mvnw
KILL_JAVA := pkill -f "java -jar" || true
endif

JAVA := java
JAR_GW := api-gateway/target/api-gateway.jar
JAR_ORD := order-service/target/order-service.jar
JAR_PAY := payment-service/target/payment-service.jar
JAR_NOT := notification-service/target/notification-service.jar

.PHONY: all build start stop docker-up docker-down test clean help

# ─── Targets ─────────────────────────────────────────────────────────────────

all: docker-up build start

build:
ifeq ($(OS),Windows_NT)
$(MVN) clean package -DskipTests
else
chmod +x mvnw
$(MVN) clean package -DskipTests
endif

docker-up:
docker-compose up -d

docker-down:
docker-compose down

# Windows: opens each service in a new terminal window.
# Linux/Mac: runs each service in the background, logging to *.log files.
start:
ifeq ($(OS),Windows_NT)
cmd /C start "API Gateway" $(JAVA) -jar $(JAR_GW)
cmd /C start "Order Service" $(JAVA) -jar $(JAR_ORD)
cmd /C start "Payment Service" $(JAVA) -jar $(JAR_PAY)
cmd /C start "Notification Service" $(JAVA) -jar $(JAR_NOT)
else
$(JAVA) -jar $(JAR_GW) > api-gateway.log 2>&1 &
$(JAVA) -jar $(JAR_ORD) > order-service.log 2>&1 &
$(JAVA) -jar $(JAR_PAY) > payment-service.log 2>&1 &
$(JAVA) -jar $(JAR_NOT) > notification-service.log 2>&1 &
@echo "Services started. Follow logs with: tail -f api-gateway.log"
endif

stop:
-$(KILL_JAVA)

test:
curl -s -X POST http://localhost:8080/api/order \
-H "Content-Type: application/json" \
-d '{"id":1,"product":"Widget","quantity":2,"price":19.99}'

clean:
$(MVN) clean

help:
@echo ""
@echo " make all - docker-up + build + start"
@echo " make build - compile and package all JARs"
@echo " make docker-up - start RabbitMQ"
@echo " make docker-down - stop RabbitMQ"
@echo " make start - launch all four services"
@echo " make stop - kill all Java processes"
@echo " make test - POST a test order to the API"
@echo " make clean - remove build artifacts"
@echo ""
40 changes: 40 additions & 0 deletions api-gateway/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.microservices.rabbitmq</groupId>
<artifactId>microservices-rabbitmq-parent</artifactId>
<version>1.0.0</version>
</parent>

<artifactId>api-gateway</artifactId>
<name>API Gateway</name>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>

<build>
<finalName>api-gateway</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.microservices.rabbitmq.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.microservices.rabbitmq.apigateway.config;

import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JavaTypeMapper;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

public static final String ORDER_QUEUE = "order_queue";

@Bean
public Queue orderQueue() {
return new Queue(ORDER_QUEUE, true);
}

@Bean
public MessageConverter messageConverter() {
Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper typeMapper = new DefaultJackson2JavaTypeMapper();
typeMapper.setTypePrecedence(Jackson2JavaTypeMapper.TypePrecedence.INFERRED);
typeMapper.setTrustedPackages("*");
converter.setJavaTypeMapper(typeMapper);
return converter;
}

@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(messageConverter());
return template;
}
}
Loading
Loading