⸻
This project builds a cloud-native microservices platform with three loosely coupled services—User, Order, Notification—demonstrating event-driven architecture, infrastructure as code, and full observability. The platform uses Kotlin (Spring Boot) or Go (Gin) for services, PostgreSQL for persistence, and Kafka (MSK) for asynchronous events. Deployment targets AWS via Terraform for reproducibility. Monitoring/alerting is provided via Prometheus and Grafana.
⸻
- End-to-end microservices lifecycle: Develop, test, deploy, monitor, and maintain microservices using best industry practices.
- Event-driven workflows: Showcase decoupling and asynchronous communication using Kafka.
- Infrastructure-as-Code: Apply modular Terraform to manage all AWS infrastructure.
- Observability: Instrument all services, collect metrics, and visualize in Grafana.
- Security & Compliance: Enforce least-privilege, encryption, and secure inter-service comms.
⸻
- Scope
- Microservices:
- user-service
- order-service
- notification-service
- Infrastructure Modules:
- VPC, RDS (PostgreSQL), MSK (Kafka), ECS/EKS, IAM, ALB/API Gateway
- Observability:
- Prometheus scraping each service, Grafana dashboards
- Documentation:
- README, architecture diagrams, example workflows
⸻
[Client]
│
▼
[API Gateway (ALB)]
│
┌─┼─────────┬────────────┐
│ │ │
▼ ▼ ▼
[User Svc] [Order Svc] [Notif Svc]
│ │ │
▼ ▼ ▼
[Postgres] [Postgres] (Stateless)
│ │
└─►[Kafka (MSK)]◄──────┘
│ │
┌────┴────┐ │
▼ ▼ ▼
(Order, User events)
- API Gateway (ALB): Routes external traffic.
- ECS Fargate: Deploys stateless services.
- Kafka (MSK): Core event bus.
- RDS PostgreSQL: Service DBs.
- Prometheus: Scrapes /metrics.
- Grafana: Visualization/alerts.
⸻
- User Service:
- Receives REST call, writes to Postgres
- Emits user.created to Kafka
- Order Service:
- Consumes user.created, processes orders
- Writes order to Postgres, emits order.processed
- Notification Service:
- Consumes events, sends emails/notifications
- Prometheus:
- Each service exposes /metrics endpoint
- Prometheus scrapes all, Grafana dashboards visualize metrics
⸻
- Backend: Kotlin (Spring Boot) or Go (Gin)
- Messaging: Kafka (AWS MSK)
- Database: PostgreSQL (RDS)
- Container Runtime: ECS Fargate or EKS (Kubernetes)
- Networking: VPC, ALB
- Infrastructure: Terraform 1.x, AWS CLI
- Observability: Prometheus, Grafana
⸻
Terraform Modules
- Root infra/
- modules/vpc/
- modules/rds/
- modules/msk/
- modules/ecs/ or modules/eks/
- modules/iam/
- modules/alb/
- environments/dev/, staging/, prod/
- State management: S3 backend + DynamoDB lock
- CI: Plan/apply via pipeline; validate on PR
⸻
- Availability: ≥ 99.5% (multi-AZ, health checks, auto-recovery)
- Latency: < 200ms avg (API/DB/queue performance tuning)
- Idempotency: Terraform applies and service APIs
- Security: TLS for Kafka, IAM for service accounts, encryption at rest (RDS, MSK)
⸻
- GitOps: All changes via PR/merge in infra/ repo; pipeline triggers terraform plan/apply
- Blue/green deploy: ECS task set swaps or k8s rolling update
- Service rollout: Health checks, automatic rollback on failure
⸻
- Service-level: Unit/integration tests (JUnit/Go test)
- E2E: Docker Compose for local integration, then ECS/EKS
- Infra: terraform validate and plan checks in CI
- Chaos testing: Randomly terminate tasks/pods to ensure resilience, use AWS Fault Injection Simulator if available
⸻
- Prometheus Exporters: Spring Boot actuator/Go promhttp on /metrics
- Prometheus Server: Runs on EC2 or ECS/EKS
- Grafana Dashboards: Pre-built, with alerts for high error rates, latency, saturation
- Drift detection: Monthly terraform plan diff vs. applied state
⸻
- Kafka (MSK): TLS, IAM authentication
- PostgreSQL: Encrypted storage, restricted security group access
- ECS/EKS: Task roles with least privilege, separate network for backend
- ALB: HTTPS only, WAF (optional)
- Secret Management: AWS Secrets Manager for DB/Kafka creds
⸻
- Monthly: Drift detection, patching base images, review IAM policies
- On-call: PagerDuty/SNS alerts for Grafana triggers
⸻
| Week | Milestone |
|---|---|
| 1 | Scaffold services (local Docker Compose) |
| 2 | Terraform infra MVP (VPC, RDS, MSK, ECS) |
| 3 | Kafka integration, end-to-end event flows |
| 4 | Observability, dashboards, documentation |
⸻
To run the microservices locally, follow these steps:
- Build the services:
./gradlew build- Start all services with Docker Compose
docker-compose -f docker-compose-local.yml upThe services will be available at:
- User Service: http://localhost:8081
- Order Service: http://localhost:8082
- Notification Service: http://localhost:8083
- PostgreSQL: localhost:5432
- Kafka: localhost:9092
⸻
- Service mesh (Istio/App Mesh) for advanced traffic management
- Canary deployments and autoscaling
- Secrets rotation automation
- Full SRE runbook for on-call
⸻
{
"event_type": "user.created",
"user_id": "123456",
"email": "user@domain.com",
"created_at": "2024-05-23T19:00:00Z"
}
http_requests_total{method="POST",endpoint="/users"} 1234
service_errors_total{service="user-service"} 3
{
"title": "User Service Dashboard",
"panels": [
{
"title": "HTTP Requests",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "http_requests_total{method=\"POST\",endpoint=\"/users\"}",
"refId": "A"
}
]
}
]
}⸻
I can provide architecture diagrams (UML, draw.io, or PlantUML format), sample Terraform modules, or boilerplate for Spring Boot/Gin microservices, as well as Prometheus/Grafana configs—just ask for the specifics you want next!
⸻
Stretch Goals:
- A specific architecture diagram (SVG, PlantUML, Markdown)
- Terraform skeleton for one of the modules
- Spring Boot or Go service boilerplate
- Example Kafka producer/consumer code
- Prometheus/Grafana YAMLs
- End-to-end test scenarios
⸻
Paste this into PlantUML Online Editor for a visual diagram.
@startuml
!define RECTANGLE class
rectangle "API Gateway (ALB)" as alb
rectangle "User Service\n(Spring Boot)" as user
rectangle "Order Service\n(Spring Boot)" as order
rectangle "Notification Service\n(Spring Boot)" as notif
database "RDS PostgreSQL" as db
cloud "Kafka (MSK)" as kafka
rectangle "Prometheus" as prometheus
rectangle "Grafana" as grafana
alb --> user : REST
alb --> order : REST
alb --> notif : REST
user --> db : JDBC
order --> db : JDBC
user --> kafka : "user.created"
order --> kafka : "order.processed"
notif --> kafka : "notification events"
kafka --> order : "user.created"
kafka --> notif : "order.processed"
user --> prometheus : "/metrics"
order --> prometheus : "/metrics"
notif --> prometheus : "/metrics"
prometheus --> grafana : "metrics"
@enduml
⸻
Directory Layout:
infra/
├── main.tf
├── variables.tf
├── outputs.tf
├── provider.tf
├── modules/
│ ├── vpc/
│ │ └── (vpc.tf, variables.tf, outputs.tf)
│ ├── rds/
│ │ └── (rds.tf, variables.tf, outputs.tf)
│ ├── msk/
│ │ └── (msk.tf, variables.tf, outputs.tf)
│ ├── ecs/
│ │ └── (ecs.tf, variables.tf, outputs.tf)
│ ├── alb/
│ │ └── (alb.tf, variables.tf, outputs.tf)
│ ├── iam/
│ │ └── (iam.tf, variables.tf, outputs.tf)
│ └── observability/
│ └── (prometheus.tf, grafana.tf)
└── environments/
├── dev/
│ └── (main.tf, variables.tf)
├── staging/
└── prod/module "vpc" {
source = "./modules/vpc"
...
}
module "rds" {
source = "./modules/rds"
vpc_id = module.vpc.vpc_id
...
}
module "msk" {
source = "./modules/msk"
vpc_id = module.vpc.vpc_id
...
}
module "ecs" {
source = "./modules/ecs"
vpc_id = module.vpc.vpc_id
...
}
module "alb" {
source = "./modules/alb"
vpc_id = module.vpc.vpc_id
...
}
module "iam" {
source = "./modules/iam"
...
}
module "observability" {
source = "./modules/observability"
...
}Each module directory (modules/vpc, modules/rds, etc.) will have its own *.tf files. Let me know if you want a sample for a specific module (e.g., full VPC or RDS).
⸻
@SpringBootApplication
class UserServiceApplication
fun main(args: Array<String>) {
runApplication<UserServiceApplication>(*args)
}@RestController
@RequestMapping("/users")
class UserController(
private val userService: UserService
) {
@PostMapping
fun createUser(@RequestBody req: CreateUserRequest): ResponseEntity<User> =
ResponseEntity.ok(userService.createUser(req))
}
data class CreateUserRequest(val email: String, val name: String)@Service
class UserService(
private val userRepository: UserRepository,
private val kafkaTemplate: KafkaTemplate<String, UserCreatedEvent>
) {
fun createUser(req: CreateUserRequest): User {
val user = userRepository.save(User(email = req.email, name = req.name))
kafkaTemplate.send("user.created", user.id.toString(), UserCreatedEvent(user.id, user.email, user.name))
return user
}
}
data class UserCreatedEvent(val id: Long, val email: String, val name: String)@Entity
data class User(
@Id @GeneratedValue val id: Long = 0,
val email: String,
val name: String
)interface UserRepository : JpaRepository<User, Long>spring:
datasource:
url: jdbc:postgresql://localhost:5432/userdb
username: user
password: pass
kafka:
bootstrap-servers: localhost:9092
management:
endpoints:
web:
exposure:
include: health,info,prometheus@Configuration
class KafkaConfig {
@Bean
fun kafkaTemplate(producerFactory: ProducerFactory<String, UserCreatedEvent>): KafkaTemplate<String, UserCreatedEvent> =
KafkaTemplate(producerFactory)
}Add dependency:
implementation("io.micrometer:micrometer-registry-prometheus")and /actuator/prometheus will expose metrics.
⸻
// Inside UserService, see above
kafkaTemplate.send("user.created", user.id.toString(), UserCreatedEvent(user.id, user.email, user.name))@Component
class UserEventListener {
@KafkaListener(topics = ["user.created"], groupId = "order-service")
fun handleUserCreated(event: String) {
val userEvent = ObjectMapper().readValue(event, UserCreatedEvent::class.java)
// process userEvent
}
}kafkaTemplate.send("order.processed", order.id.toString(), OrderProcessedEvent(...))@Component
class OrderEventListener {
@KafkaListener(topics = ["order.processed"], groupId = "notification-service")
fun handleOrderProcessed(event: String) {
val orderEvent = ObjectMapper().readValue(event, OrderProcessedEvent::class.java)
// send notification
}
}@Configuration
class KafkaConfig {
@Bean
fun consumerFactory(): ConsumerFactory<String, String> {
val props = HashMap<String, Any>()
props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = "localhost:9092"
props[ConsumerConfig.GROUP_ID_CONFIG] = "order-service"
props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java
props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java
return DefaultKafkaConsumerFactory(props)
}
@Bean
fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, String> {
val factory = ConcurrentKafkaListenerContainerFactory<String, String>()
factory.consumerFactory = consumerFactory()
return factory
}
}⸻
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'user-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['user-service:8080']
- job_name: 'order-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['order-service:8080']
- job_name: 'notification-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['notification-service:8080']- Add Prometheus as a data source (URL:
http://prometheus:9090) - Import Dashboards: Use Spring Boot Micrometer dashboards or build custom ones for:
- Request rate, latency, error rates per service
- JVM memory/cpu stats (for Kotlin services)
- Kafka consumer lag (dashboard example)
version: "3"
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin⸻
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:kafka")
testImplementation("org.testcontainers:postgresql")@SpringBootTest
@AutoConfigureMockMvc
@Testcontainers
class UserOrderE2ETest {
companion object {
@Container
val postgres = PostgreSQLContainer("postgres:13")
@Container
val kafka = KafkaContainer("5.5.1")
}
@Autowired lateinit var mockMvc: MockMvc
@Test
fun `end-to-end user creation emits event and triggers order service`() {
// 1. Create user via REST
val userJson = """{"email":"test@acme.com","name":"Jane"}"""
mockMvc.perform(
post("/users").contentType(MediaType.APPLICATION_JSON).content(userJson)
).andExpect(status().isOk)
// 2. Assert that a 'user.created' event was published to Kafka
// (Implement a Kafka consumer to listen and verify the event)
// 3. Mock or spin up order service to consume and process event
// 4. Verify order processed event and notification sent (simulate downstream)
}
}- Full integration with real Kafka and Postgres
- Test notification flow via a mock SMTP server (e.g. GreenMail)
- Chaos tests: Randomly kill service containers during tests and assert recovery
⸻
What Next?
- Architecture Diagram: Use PlantUML code above for high-level architecture.
- Terraform: Copy-paste skeleton; ask for any module in detail.
- Spring Boot: Use code as template; let me know if you want the Order/Notification variants.
- Kafka: Producer/Consumer code included.
- Prometheus/Grafana: Configs and dashboards ready; expand as needed.
- End-to-End Tests: Copy pattern above for each microservice pair.