Web Solutions

Microservices Architecture in Java — Design, Structure & Kafka Integration

·

·

As a Java backend engineer with over a decade in software engineering, I’ve seen monoliths turn into bottlenecks, deployments break entire systems, and business domains tightly coupled in ways that made scaling painful. That’s why microservices — when done right — solve more than just technical problems. They align your codebase with your business.

In this post, I’ll walk through:

  • ✅ What microservices are (and are not)
  • 🏗️ How to design and structure a microservices-based Java application
  • 🔄 How to integrate Kafka for event-driven communication
  • 🧪 Code samples to make it practical

🚀 What Are Microservices?

Microservices are small, independent services that run in their own process, own their data, and communicate over well-defined APIs (often REST or messaging). Each service is focused on a specific business capability.

Characteristics:

  • Independently deployable
  • Own database (no shared DB!)
  • Loosely coupled, highly cohesive
  • Can be developed and scaled independently

🏗️ Microservices Design — High-Level Structure

Imagine we’re building an e-commerce system. We’d break it into services like:

  • UserService – manages user profiles and authentication
  • ProductService – manages product catalog
  • OrderService – handles order placement
  • InventoryService – keeps track of stock
  • NotificationService – sends emails/SMS on events

Each service:

  • Exposes a REST API (via Spring Boot)
  • Has its own database
  • Publishes/subscribes to Kafka topics for asynchronous workflows

📦 Directory Structure (Per Service)

product-service/
├── src/main/java/com/myapp/product
│ ├── controller
│ ├── service
│ ├── repository
│ ├── model
│ └── event
├── src/main/resources
│ └── application.yml
└── Dockerfile

⚙️ Kafka in Microservices

Kafka allows event-driven communication. Instead of directly calling other services (tight coupling), we publish events like OrderPlaced, and interested services subscribe.

Why use Kafka?

  • Asynchronous, decoupled architecture
  • High throughput and durability
  • Easy retry and dead-letter support
  • Enables reactive systems

🔄 Example: OrderService → Kafka → InventoryService

OrderPlacedEvent.java

public class OrderPlacedEvent {
private String orderId;
private List<String> productIds;
// getters/setters
}

OrderService Publishes Event

@Autowired
private KafkaTemplate<String, OrderPlacedEvent> kafkaTemplate;

public void placeOrder(Order order) {
// save order to DB
kafkaTemplate.send("order.placed", new OrderPlacedEvent(order.getId(), order.getProductIds()));
}

InventoryService Consumes Event

@KafkaListener(topics = "order.placed", groupId = "inventory")
public void handleOrderPlaced(OrderPlacedEvent event) {
for (String productId : event.getProductIds()) {
inventoryService.decreaseStock(productId);
}
}

🔐 Configuration Example (application.yml)

spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: inventory
auto-offset-reset: earliest
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer

🧠 Design Best Practices

  1. Domain-Driven Design: Structure services around business domains, not technical layers.
  2. API Contracts: Define REST or event contracts clearly — use OpenAPI and JSON schema.
  3. Resilience: Use retry patterns, circuit breakers (e.g., Resilience4j).
  4. Observability: Implement centralized logging, metrics, and tracing (ELK, Prometheus, Zipkin).
  5. Security: Use API gateways with JWT or OAuth2 for inter-service authentication.

🧪 Tools You’ll Commonly Use

  • Spring Boot – fast microservice scaffolding
  • Spring Cloud – config server, discovery, gateway
  • Kafka – async event backbone
  • Docker – containerization
  • Kubernetes – deployment and orchestration
  • GitHub Actions / GitLab CI – CI/CD automation

✅ Final Thoughts

Microservices bring flexibility, scalability, and speed, but require strong discipline. With the right patterns — like event-driven design with Kafka — your services become decoupled, resilient, and built for change.

Start small. Extract one domain (like OrderService) into its own service, define its API, set up Kafka communication, and scale from there.

Done well, microservices don’t just improve code — they transform how your teams build and release software.


Leave a Reply

Your email address will not be published. Required fields are marked *