A polyglot microservices architecture demonstrating synchronous and asynchronous communication patterns using different technology stacks and AWS services via LocalStack.
This project consists of two microservices that demonstrate modern microservice patterns:
- Service 1: Counter API (Kotlin/Spring Boot) - Handles HTTP requests and manages a simple counter
- Service 2: Message Processor (PHP/Symfony) - Processes events asynchronously and sends email reports
Client β GET /counter β Service 1 β PostgreSQL (returns current total)
Client β POST /counter/increment β Service 1 β PostgreSQL (+1) β SQS Message
SQS Message β Symfony Messenger β MongoDB Document β Hourly Job β SES Email β Clear MongoDB
- Language: Kotlin
- Framework: Spring Boot 3.x
- Database: PostgreSQL
- Features: Spring Data JPA, Spring Web, OpenAPI 3, JUnit 5
- Purpose: REST API for counter operations with comprehensive testing
- Language: PHP 8.4
- Framework: Symfony 7.x
- Message Queue: Symfony Messenger + SQS transport
- Database: MongoDB
- Email: AWS SES integration
- Error Tracking: Sentry integration
- Purpose: Event collection and hourly email reports
- Message Queue: AWS SQS via LocalStack
- Email Service: AWS SES via LocalStack
- Containerization: Docker & Docker Compose
- Local Development: LocalStack for AWS services simulation
microservice-test/
βββ counter-api/ # Service 1 (Kotlin/Spring Boot)
β βββ src/main/kotlin/com/example/counter/
β β βββ CounterApplication.kt
β β βββ controller/CounterController.kt
β β βββ service/CounterService.kt
β β βββ repository/CounterRepository.kt
β β βββ entity/Counter.kt
β β βββ dto/CounterResponseDto.kt
β β βββ config/
β β βββ DatabaseConfig.kt
β β βββ SqsConfig.kt
β βββ src/test/kotlin/com/example/counter/
β β βββ CounterApplicationTests.kt
β β βββ controller/CounterControllerTest.kt
β β βββ service/CounterServiceTest.kt
β β βββ repository/CounterRepositoryTest.kt
β βββ src/main/resources/
β β βββ application.yml
β β βββ application-test.yml
β β βββ db/migration/V1__create_counter_table.sql
β βββ build.gradle.kts
β βββ Dockerfile
βββ message-processor/ # Service 2 (PHP/Symfony)
β βββ src/
β β βββ MessageHandler/CounterIncrementHandler.php
β β βββ Document/CounterEvent.php
β β βββ Service/EmailService.php
β β βββ Command/ProcessHourlyEmailCommand.php
β β βββ Repository/CounterEventRepository.php
β βββ config/
β β βββ packages/messenger.yaml
β β βββ packages/doctrine_mongodb.yaml
β β βββ services.yaml
β βββ composer.json
β βββ symfony.lock
β βββ .env
β βββ Dockerfile
βββ docker-compose.yml
βββ README.md
CREATE TABLE counter (
id INTEGER PRIMARY KEY DEFAULT 1,
value INTEGER NOT NULL DEFAULT 0,
CONSTRAINT single_row CHECK (id = 1)
);
INSERT INTO counter (id, value) VALUES (1, 0);// Collection: counter_events
{
"_id": ObjectId,
"eventType": "COUNTER_INCREMENT",
"timestamp": ISODate("2024-01-15T10:30:00Z"),
"createdAt": ISODate("2024-01-15T10:30:05Z"),
"metadata": {
"source": "counter-api",
"version": "1.0"
}
}GET /counter- Returns current counter value:{"value": 42}POST /counter/increment- Increments counter, sends SQS message, returns new valueGET /health- Health check endpointGET /swagger-ui.html- OpenAPI documentation
{
"eventType": "COUNTER_INCREMENT",
"timestamp": "2024-01-15T10:30:00Z",
"metadata": {
"source": "counter-api",
"version": "1.0"
}
}Service 2 generates hourly email reports with the following content:
Subject: Hourly Counter Report - [Date Hour]
Counter Activity Summary:
- Total increments this hour: 15
- Events processed: 15
- Time period: 2024-01-15 10:00-11:00 UTC
First event: 10:05:23 UTC
Last event: 10:58:47 UTC
All events have been processed and cleared from the system.
- Database per Service: PostgreSQL vs MongoDB for different use cases
- Event-driven Architecture: Asynchronous communication via SQS
- Polyglot Persistence: Different storage solutions for different needs
- Batch Processing: Hourly aggregation and cleanup operations
- Kotlin/Spring Boot: Modern JVM microservice development with comprehensive testing
- PHP/Symfony: Modern PHP with Symfony Messenger for message handling
- PostgreSQL: Relational data management with atomic operations
- MongoDB: Document-based event storage with temporal data
- LocalStack: AWS services simulation for local development
- Synchronous: REST API calls with proper error handling
- Asynchronous: SQS message queues with reliable processing
- Scheduled Tasks: Cron-based email notifications with cleanup
- Event Sourcing: Store events then process and clean pattern
- Docker & Docker Compose
- JDK 17+ (for Kotlin service)
- PHP 8.4+ & Composer (for PHP service)
- LocalStack CLI (optional, for debugging)
# Clone the repository
git clone <repository-url>
cd microservice-test
# Start all services with Docker Compose
docker-compose up -d
# Check service health
curl http://localhost:8080/health # Counter API
curl http://localhost:8080/counter # Get current counter value
# Increment counter (triggers async processing)
curl -X POST http://localhost:8080/counter/increment# Service 1 (Kotlin) - Local development
cd counter-api
./gradlew bootRun
# Service 2 (PHP) - Local development
cd message-processor
composer install
symfony server:start
# Run message consumer
php bin/console messenger:consume sqs
# Run hourly email job (manually)
php bin/console app:process-hourly-emailcd counter-api
# Run all tests
./gradlew test
# Run integration tests with TestContainers
./gradlew integrationTest
# Generate test coverage report
./gradlew jacocoTestReportcd message-processor
# Run PHPUnit tests
php bin/phpunit
# Run specific test suites
php bin/phpunit tests/MessageHandler/- SQS Console:
http://localhost:4566/_localstack/sqs - SES Console:
http://localhost:4566/_localstack/ses - Health:
http://localhost:4566/_localstack/health
# View service logs
docker-compose logs -f counter-api
docker-compose logs -f message-processor
# MongoDB operations
docker-compose exec mongodb mongoshLOCALSTACK_ENDPOINT: LocalStack endpoint URLSQS_QUEUE_URL: SQS queue URL for message processingDATABASE_URL: PostgreSQL connection stringMONGODB_URL: MongoDB connection stringSES_FROM_EMAIL: Email sender address for notificationsSENTRY_DSN: Sentry DSN for error tracking (optional)SENTRY_TRACES_SAMPLE_RATE: Performance monitoring sample rate (0.0-1.0, default: 0.0)SENTRY_PROFILES_SAMPLE_RATE: Profiling sample rate (0.0-1.0, default: 0.0)SENTRY_RELEASE: Release version for tracking deployments (optional)SENTRY_SERVER_NAME: Server name identifier (default: message-processor)
The Message Processor service includes Sentry integration for error tracking and monitoring. To enable Sentry:
-
Get your Sentry DSN:
- Sign up at sentry.io
- Create a new project (PHP/Symfony)
- Copy your DSN from the project settings
-
Configure environment variables:
# In your .env file or docker-compose.yml SENTRY_DSN=https://your-key@sentry.io/your-project-id SENTRY_TRACES_SAMPLE_RATE=0.1 # 10% of transactions SENTRY_RELEASE=1.0.0 # Optional: track releases
-
Sentry automatically captures:
- Unhandled exceptions in console commands
- Errors in message handlers
- AWS SES exceptions with context
- Symfony Messenger failures
-
Manual error capture (already implemented):
// In your services, Sentry Hub is automatically injected if ($this->sentryHub !== null) { $this->sentryHub->captureException($exception, [ 'tags' => ['service' => 'email'], 'contexts' => ['custom' => 'data'], ]); }
This project demonstrates:
- Polyglot Microservices: Different languages solving different problems
- Event-driven Architecture: Loose coupling through messaging
- Different Data Models: SQL for state, NoSQL for events
- Comprehensive Testing: Unit, integration, and contract testing
- Modern Frameworks: Latest versions of Spring Boot and Symfony
- Cloud-native Patterns: AWS services integration via LocalStack
- Production Readiness: Proper logging, health checks, and monitoring
- Service 1 + Tests: 4-5 hours
- Service 2 + Symfony: 3-4 hours
- Integration & Docker: 1-2 hours
- Total: 8-11 hours
Perfect for weekend learning or spread across multiple evening sessions!
- Fork the repository
- Create a feature branch
- Implement changes with tests
- Submit a pull request
This project is for educational purposes. Feel free to use and modify as needed for learning microservices architecture.