A comprehensive Spring Boot microservice for managing foreign exchange (FX) rate subscriptions with real-time notifications, MCP (Model Context Protocol) server capabilities, and event-driven architecture.
- User Management: Complete user registration, authentication, and profile management
- FX Subscription Management: Create, update, delete, and monitor currency pair subscriptions
- Real-time Notifications: Multi-channel notification support (email, SMS, push)
- MCP Server Integration: Model Context Protocol server for AI tool interactions
- Event-Driven Architecture: Kafka-based event publishing for subscription changes
- Security: JWT-based authentication with role-based access control
- Database: PostgreSQL database with JPA/Hibernate
- Caching: Redis cache with configurable TTL and Jackson serialization
- API Documentation: OpenAPI 3.1.0 specification with Swagger UI and automated documentation generation
- Monitoring: Prometheus metrics and health endpoints
- Observability: Distributed tracing with Jaeger, Logging with Loki - integrated with Grafana OSS
- Scheduling: Automated subscription processing and event publishing
- Testing: Comprehensive unit and integration tests with TestContainers for PostgreSQL, Kafka, and Redis
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web Client │ │ Mobile App │ │ AI Chat Bot │
└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌────────────▼─────────────-┐
│ FX Subscription Service │
│ (MCP Server) │
│ │
│ ┌─────────────────────┐ │
│ │ REST Controllers │ │
│ └─────────┬───────────┘ │
│ │ │
│ ┌─────────▼───────────┐ │
│ │ Business Logic │ │
│ │ (with Caching) │ │
│ └─────────┬───────────┘ │
│ │ │
│ ┌─────────▼───────────┐ │
│ │ Data Access Layer │ │
│ └─────────┬───────────┘ │
└────────────┼──────────────┘
│
┌────────────▼─────────────-┐
│ Redis Cache │
│ - subscription │
│ - subscriptionsByUser │
└───────────────────────────┘
│
┌────────────▼─────────────-┐
│ PostgreSQL DB │
└──────────────────────────-┘
│
┌────────────▼─────────────-┐
│ Kafka Topics │
│ - subscription-changes │
└───────────────────────────┘
- Docker and Docker Compose
- A valid PKCS12 keystore file (for SSL/TLS)
- Environment variables configured (see Configuration section)
- Java 25+
- Gradle 9.4.1+
- PostgreSQL 18.3+
- Redis 8.6.2+
- Apache Kafka 4.2.0+
- Docker Compose (for observability stack)
- FX MCP Client (for AI interactions)
This is the fastest way to get the entire application stack running locally with minimal setup.
git clone <repository-url>
cd fx-subscription-serviceCreate a .env file in the project root or set the following environment variables:
# Required: SSL Keystore Configuration
export FX_KEYSTORE_LOCATION=path/to/your/keystore.p12
export FX_KEYSTORE_PASSWORD=your_keystore_password
# Required: JWT Secret Key
export FX_JWT_SECRET_KEY=your_jwt_secret_key_at_least_256_bits
# Optional: Database Configuration (defaults provided)
# export FX_POSTGRES_DB=fx_subscription_db
# export FX_POSTGRES_USER=postgres
# export FX_POSTGRES_PASSWORD=password
# Optional: Redis Configuration (defaults provided)
# export FX_REDIS_HOST=redis://redis:6379Run below command to create a self-signed ssl cert locally:
keytool -genkeypair \
-alias fx-subscription-service \
-keyalg RSA \
-keysize 4096 \
-storetype PKCS12 \
-keystore keystore.p12 \
-validity 365 \
-dname "CN=localhost, OU=Dev, O=Local, L=Local, ST=Local, C=GB" \
-storepass changeit \
-keypass changeit \
-ext "SAN=dns:localhost,ip:127.0.0.1"Also, create a strong JWT secret key (if needed):
openssl rand -base64 64# This script runs tests, builds, and starts all services
chmod +x build_and_run.sh
./build_and_run.shWhat this script does:
- ✅ Runs all tests on the host machine
- ✅ Generates OpenAPI documentation
- ✅ Runs JaCoCo coverage verification
- ✅ Performs code quality checks
- ✅ Builds and starts all Docker services including:
- FX Subscription Service (main application)
- PostgreSQL database
- Apache Kafka (message broker)
- Observability stack (Grafana, Prometheus, Loki, Jaeger)
# Check all services status
docker compose ps
# View logs
docker compose logs web-service
docker compose logs postgres
docker compose logs kafkaIf you prefer more control over the setup process:
# Build and start main application with database and Kafka
docker compose up --build -d
# Check service status
docker compose ps# Start monitoring and observability tools
docker compose -f docker-compose.observability.yaml up -d
# Verify observability services
docker compose -f docker-compose.observability.yaml psFor developers who prefer running the application directly on their host machine:
# Start only PostgreSQL and Kafka via Docker
docker compose up postgres kafka -d
# Or install PostgreSQL and Kafka natively on your systemUpdate src/main/resources/application.yaml with your local database and Kafka settings.
# Build the application
./gradlew build
# Run the application
./gradlew bootRunAfter successful setup, the following services will be available:
- FX Subscription Service: https://localhost:8443
- OpenAPI Docs: https://localhost:8443/v3/api-docs
- Health Check: https://localhost:8443/actuator/health
- Metrics: https://localhost:8443/actuator/prometheus
- PostgreSQL: localhost:5432
- Database:
fx_subscription_db - Username:
postgres - Password:
password
- Database:
- Redis: localhost:6379
- Cache:
subscription,subscriptionsByUser
- Cache:
- Kafka: localhost:9092
- Topic:
subscription-change-events
- Topic:
- Grafana Dashboard: http://localhost:3000 (admin/admin)
- Prometheus Metrics: http://localhost:9090
- Jaeger Traces: http://localhost:16686
- Loki Logs: http://localhost:3100
- SSE Endpoint: https://localhost:8443/sse (for FX MCP Client)
-
Code Changes: Make your changes to the source code
-
Test Locally: Run tests with
./gradlew test -
Rebuild Service:
# Rebuild just the web service docker compose up web-service --build # Or use the full build script ./build_and_run.sh
-
Load Testing (Optional): Run load tests with
./gradlew :fx-subscription-service-gatling:gatlingRun
# View application logs
docker compose logs -f web-service
# Access database directly
docker exec -it postgres psql -U postgres -d fx_subscription_db
# Access Redis cache
docker exec -it redis redis-cli
# Check Kafka topics
docker exec -it kafka kafka-topics.sh --bootstrap-server localhost:9092 --list# Stop all services
docker compose down
# Stop with observability stack
docker compose -f docker-compose.yaml -f docker-compose.observability.yaml down
# Stop and remove volumes (⚠️ This will delete your data)
docker compose down -vThe project includes several Docker configuration files for different purposes:
Dockerfile: Production-ready container imageDockerfile.local: Multi-stage build for local developmentdocker-compose.yaml: Core services (app, database, Kafka)docker-compose.observability.yaml: Monitoring and observability stackbuild_and_run.sh: Automated setup script for complete local development environment
## ⚙️ Configuration
### Environment Variables for Docker Setup
The Docker-based setup uses the following environment variables:
```bash
# Required: SSL Configuration
FX_KEYSTORE_LOCATION=path/to/your/keystore.p12
FX_KEYSTORE_PASSWORD=your_keystore_password
# Required: JWT Configuration
FX_JWT_SECRET_KEY=your_jwt_secret_key_at_least_256_bits
# Optional: Database Configuration (defaults shown)
FX_POSTGRES_HOST=postgres:5432
FX_POSTGRES_DB=fx_subscription_db
FX_POSTGRES_USER=postgres
FX_POSTGRES_PASSWORD=password
# Optional: Redis Configuration (defaults shown)
FX_REDIS_HOST=redis://redis:6379
# Optional: Kafka Configuration (defaults shown)
FX_KAFKA_HOST=kafka:29092
# Optional: Tracing Configuration (defaults shown)
FX_JAEGER_HOST=http://jaeger:4318
# SSL Configuration
server.port=8443
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=${FX_KEYSTORE_PASSWORD}
server.ssl.key-store-type=PKCS12
# JWT Configuration
security.jwt.token.secret-key=${FX_JWT_SECRET_KEY}
security.jwt.token.expire-length=3600000
# Database (PostgreSQL)
spring.datasource.url=jdbc:postgresql://localhost:5432/fx_subscription_db
spring.datasource.username=postgres
spring.datasource.password=password
# Redis Cache Configuration
spring.cache.type=redis
spring.cache.redis.time-to-live-seconds=300
spring.data.redis.host=localhost
spring.data.redis.port=6379
# Kafka Configuration
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.topic.subscription-changes=subscription-change-events
# MCP Server Configuration
#spring.ai.mcp.server.name=fx-mcp-serverThe service automatically generates OpenAPI 3.1.0 documentation using SpringDoc. The documentation is available at:
- Swagger UI:
https://localhost:8443/swagger-ui.html - OpenAPI JSON:
https://localhost:8443/v3/api-docs - Generated Documentation:
api-docs/fx-subscription-service.json
POST /api/v1/auth/signup
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123",
"mobile": "+1234567890",
"admin": false
}Password Requirements:
- Must contain at least one word character (a-z, A-Z, 0-9, _)
- Must be at least 8 characters long
- Example valid passwords:
password123,MyPass_123,SecurePass1
POST /api/v1/auth/login
Content-Type: application/json
{
"username": "user@example.com",
"password": "password123"
}POST /api/v1/subscriptions
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"currencyPair": "GBP/USD",
"threshold": 1.25,
"direction": "ABOVE",
"notificationChannels": ["email", "sms"]
}GET /api/v1/subscriptions/all
Authorization: Bearer <jwt_token>GET /api/v1/subscriptions?userId={userId}
Authorization: Bearer <jwt_token>GET /api/v1/subscriptions/my
Authorization: Bearer <jwt_token>PUT /api/v1/subscriptions/{id}
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"currencyPair": "EUR/USD",
"threshold": 1.15,
"direction": "BELOW",
"status": "ACTIVE",
"notificationChannels": ["email"]
}Note: The status field is optional and can be "ACTIVE", "INACTIVE", or "EXPIRED".
DELETE /api/v1/subscriptions/{id}
Authorization: Bearer <jwt_token>GET /api/v1/users?page=0&size=20
Authorization: Bearer <jwt_token>GET /api/v1/users/{id}
Authorization: Bearer <jwt_token>GET /api/v1/users/search
Authorization: Bearer <jwt_token>GET /api/v1/users/{id}/subscriptions
Authorization: Bearer <jwt_token>PUT /api/v1/users/{id}
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"email": "newemail@example.com",
"mobile": "+9876543210",
"pushDeviceToken": "optional_push_device_token"
}Note: All fields are optional. The mobile field must follow international format (+1234567890).
GET /sseNote: These endpoints are used by the FX MCP Client for AI tool interactions and are not meant for direct human consumption.
id(UUID, Primary Key)email(String, Unique)mobile(String)password(String, Encrypted)enabled(Boolean)push_device_token(String)role(Enum: USER, ADMIN)created_at(Timestamp)updated_at(Timestamp)
id(UUID, Primary Key)user_id(UUID, Foreign Key)currency_pair(String)threshold(Decimal)direction(Enum: ABOVE, BELOW)notifications_channels(JSON Array)status(Enum: ACTIVE, INACTIVE, EXPIRED)created_at(Timestamp)updated_at(Timestamp)
id(UUID, Primary Key)aggregate_type(String)aggregate_id(UUID)event_type(String)payload(JSON)status(String)timestamp(Long)
- JWT-based token authentication
- BCrypt password hashing
- Role-based access control (USER, ADMIN)
- Method-level security with
@PreAuthorizeand jwt claims - Users can only access their own resources
- Admin access for system-wide operations
- HTTPS enabled by default
- Custom keystore(PKCS12) configuration
- MCP endpoints (
/sse,/mcp/**) are publicly accessible - No authentication required for MCP client connections
- Tool execution is handled securely within the service
GET /actuator/health- Application health statusGET /actuator/prometheus- Prometheus metrics
- HTTP request metrics
- Database connection metrics
- Custom business metrics
- Request Tracing: End-to-end request flow visualization
- Performance Analysis: Latency breakdown by service
- Error Tracking: Trace error propagation across services
- Structured Logging: JSON format with correlation IDs
- Log Queries: Powerful query language for log analysis
- Log Retention: Configurable retention policies
- Application Metrics: HTTP requests, response times, error rates
- Database Metrics: Connection pool, query performance
- Business Metrics: Subscription creation, user activity
- Custom Dashboards: FX-specific metrics and alerts
- SSE Communication: Server-Sent Events for real-time MCP client communication
- Tool Integration: AI tools for subscription management via MCP protocol
- Security: MCP endpoints are publicly accessible for client connections
The service exposes the following tools for AI clients:
createFxSubscription(userId, currencyPair, thresholdValue, direction, notificationMethod)- Creates a new FX rate subscriptionupdateFxSubscription(subscriptionId, newThresholdValue, direction, newNotificationMethod)- Updates an existing subscriptiondeleteFxSubscription(subscriptionId)- Deletes a subscriptiongetFxSubscriptionsForUser(userId)- Retrieves all subscriptions for a user
- MCP Server: This service acts as an MCP server
- FX MCP Client: Separate service that connects to this MCP server
- Tool Execution: AI tools are executed through the MCP protocol
- Natural Language: AI interactions are handled by the MCP client
- Topic:
subscription-change-events - Events: SubscriptionCreated, SubscriptionUpdated, SubscriptionDeleted
- Outbox Pattern: Reliable event publishing
- Subscription Processing: Every 5 minutes (configurable)
- Event Publishing: Automatic outbox processing
- Status Updates: Subscription lifecycle management
- Unit Tests: Comprehensive unit tests for all services
- Integration Tests: Controller and service integration tests
- Repository Tests: Data access layer testing with TestContainers
- Cache Tests: Redis cache integration tests with TestContainers
- Security Tests: JWT and security configuration testing
The project uses TestContainers for reliable, isolated testing:
- PostgreSQL:
PostgreSQLContainerfor database tests - Redis:
RedisContainerfor cache tests - Kafka:
KafkaContainerfor messaging tests - Test Profiles: Dedicated test profiles for each container type
# Run all tests
./gradlew test
# Run specific test class
./gradlew test --tests SubscriptionsControllerIT
# Run with coverage
./gradlew test jacocoTestReportUse the automated build script for complete local setup:
# Quick setup - runs tests, builds, and starts all services
./build_and_run.sh# Build production Docker image
docker build -t fx-subscription-service .
# Run container with external dependencies
docker run -d \
--name fx-subscription-service \
-p 8443:8443 \
-e FX_POSTGRES_HOST=your-postgres-host:5432 \
-e FX_KAFKA_HOST=your-kafka-host:9092 \
-e FX_KEYSTORE_PASSWORD=your_keystore_password \
-e FX_JWT_SECRET_KEY=your_jwt_secret \
--mount type=secret,source=keystore_p12 \
fx-subscription-serviceFor complete stack deployment including dependencies:
# Local development with full observability
docker compose -f docker-compose.yaml -f docker-compose.observability.yaml up -d
# Production-like setup with core services only
docker compose up -d- Virtual threads enabled
- Connection pooling
- Lazy loading for associations
- Transaction management
- Response time: < 100ms (95th percentile)
- Throughput: 1000+ requests/second
- Cache hit ratio: > 90% for subscription data
- Database queries: Optimized with indexes and caching
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/fx/subscription/service/
│ │ │ ├── controller/ # REST controllers
│ │ │ ├── service/ # Business logic
│ │ │ ├── repository/ # Data access
│ │ │ ├── model/ # Entities
│ │ │ ├── dto/ # Data transfer objects
│ │ │ ├── config/ # Configuration classes
│ │ │ │ ├── CacheConfig.java # Redis cache configuration
│ │ │ │ └── OpenApiConfig.java # OpenAPI documentation configuration
│ │ │ ├── exception/ # Custom exceptions
│ │ │ └── ai/ # MCP tool integration
│ │ │ └── tool/ # AI tools for MCP
│ │ └── resources/
│ │ ├── application.yaml
│ │ └── logback-spring.xml
│ └── test/
│ ├── java/ # Test classes
│ └── resources/
│ └── application.yaml
├── fx-subscription-service-gatling/ # Gatling load testing subproject
│ ├── src/gatling/java/ # Gatling simulation classes
│ │ └── com/example/fx/subscription/service/gatling/
│ │ ├── feeders/ # Data feeders for test data
│ │ └── simulations/ # Load test simulation scenarios
│ ├── src/gatling/resources/
│ │ └── gatling.conf # Gatling configuration (API endpoints, etc.)
│ └── build.gradle # Gatling-specific build configuration
├── api-docs/ # Generated OpenAPI documentation
├── build_and_run.sh # Automated setup script
├── create_github_release_backfill.sh # GitHub release automation script
├── delete-old-ghcr-images.sh # Container registry cleanup script
├── docker-compose.yaml # Core services (app, DB, Kafka)
├── docker-compose.observability.yaml # Monitoring stack
├── Dockerfile # Production container image
├── Dockerfile.local # Local development container
├── prometheus.yaml # Prometheus configuration
└── gradle/ # Gradle wrapper
- SonarQube integration
- Code formatting with Checkstyle
- 95% plus code and line coverage with JaCoCo
- Automated quality checks in build pipeline
The project includes comprehensive load testing using Gatling, now organized as a separate subproject for better maintainability.
# Run Gatling load tests
./gradlew :fx-subscription-service-gatling:gatlingRun
# Run specific simulation
./gradlew :fx-subscription-service-gatling:gatlingRun -Dgatling.simulationClass=com.example.fx.subscription.service.gatling.simulations.SubscriptionsApiSimulation
# Generate load test reports
./gradlew :fx-subscription-service-gatling:gatlingReportThe Gatling tests include:
- User Registration: Simulates user signup with various data patterns
- Authentication: Tests login performance under load
- Subscription Management: Creates, updates, and deletes subscriptions
- API Performance: Measures response times and throughput
After running tests, reports are generated in:
fx-subscription-service-gatling/build/reports/gatling/- Open
index.htmlin a web browser to view detailed performance metrics
Load tests are automatically executed via GitHub Actions:
- Manual Trigger: Use the "Gatling Load Tests" workflow
- Automated Reports: Test results are uploaded as artifacts
- Performance Monitoring: Track performance regressions over time
The fx-subscription-service-gatling/src/gatling/resources/gatling.conf file contains:
api {
local {
baseUrl = "https://localhost:8443"
signupEndpoint = "/api/v1/auth/signup"
loginEndpoint = "/api/v1/auth/login"
subscriptionsEndpoint = "/api/v1/subscriptions"
mySubscriptionsEndpoint = "/api/v1/subscriptions/my"
contentType = "application/json"
acceptType = "application/json"
}
}Configuration Options:
- baseUrl: Target application URL for load testing
- Endpoints: API endpoint paths for different operations
- Content Types: Request/response content type configuration
Environment-Specific Configuration:
You can add additional environments (e.g., staging, production) by extending the configuration:
api {
local {
baseUrl = "https://localhost:8443"
# ... other local config
}
staging {
baseUrl = "https://staging.example.com"
# ... staging-specific config
}
production {
baseUrl = "https://api.example.com"
# ... production-specific config
}
}Then run tests against specific environments:
./gradlew :fx-subscription-service-gatling:gatlingRun -Dgatling.env=staging# Complete local development setup
chmod +x build_and_run.sh
./build_and_run.shWhat this script does:
- ✅ Runs all tests on the host machine
- ✅ Generates OpenAPI documentation
- ✅ Runs JaCoCo coverage verification
- ✅ Performs code quality checks
- ✅ Builds and starts all Docker services including:
- FX Subscription Service (main application)
- PostgreSQL database
- Apache Kafka (message broker)
- Observability stack (Grafana, Prometheus, Loki, Jaeger)
# Clean up old container images (keeps last 4 versions)
export FX_GITHUB_TOKEN=your_github_token
./delete-old-ghcr-images.shPurpose: Automatically removes old container image versions from GitHub Container Registry to manage storage costs.
# Create GitHub releases for existing Git tags
./create_github_release_backfill.shPurpose: Creates GitHub releases for existing Git tags that don't have corresponding releases.
All scripts require appropriate permissions:
- build_and_run.sh: No special requirements
- delete-old-ghcr-images.sh: Requires
FX_GITHUB_TOKENenvironment variable - create_github_release_backfill.sh: Requires GitHub CLI (
gh) authentication
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Create an issue in the repository
- Check the documentation
- Review existing issues and discussions
- v1.0.0 - Initial release with core subscription management
- v1.1.0 - Added AI integration and improved security
- v1.2.0 - Enhanced monitoring and observability features with Grafana stack
- v1.3.0 - Migrated to PostgreSQL with TestContainers for reliable testing
- v2.0.0 - Refactored to MCP server architecture, moved AI chat to separate client
- v2.0.3 - Added Redis caching, enhanced TestContainers integration, and improved DTOs
- v2.1.0 - Enhanced Docker setup with automated build script and comprehensive local development environment
- v3.0.0 - Moved Gatling load testing to separate subproject for better maintainability
- v3.1.0 - Added utility scripts for container registry management and release automation
- v3.2.0 - Current version with enhanced load testing capabilities and improved CI/CD workflows
Note: This service now provides a complete containerized development environment with automated setup via build_and_run.sh. The Docker-based setup includes PostgreSQL, Redis, Kafka, and full observability stack (Grafana, Prometheus, Loki, Jaeger) for a production-like local development experience. Redis caching has been implemented for improved performance and reduced database load. The AI chat functionality is handled by a separate FX MCP Client service.