When Java microservices pay off
Microservices help when you have clear domain boundaries, independent release cadences, and a real need to scale parts of the system differently. For a small product with one team, splitting a monolith often adds operational cost without shipping faster.
A mature path looks like this: start with a modular monolith and explicit package/API boundaries, then extract services along bounded contexts. That evolution model is what you usually hear from DDD practitioners and conference talks rather than a big-bang rewrite.
Service boundaries and contracts
A healthy service owns its data and publishes a stable contract. For sync APIs use OpenAPI with versioning (/api/v1). For async collaboration use events with an explicit schema (Avro/JSON Schema) and backward-compatible field evolution.
- Do not share a database across services — that is hidden coupling.
- Write idempotency is mandatory when retries and outbox patterns are in play.
- Use sagas only for true distributed business transactions; otherwise prefer local transactions + eventual consistency.
A stack you actually see in production
A common 2024–2026 Java stack: Java 21 LTS, Spring Boot 3.x (or Quarkus when startup/memory dominate), PostgreSQL/MySQL, Redis, Kafka/RabbitMQ, Docker + Kubernetes.
Spring Cloud Gateway or an ingress-level API gateway handles routing, rate limits, and TLS. Inside Kubernetes you usually do not need Netflix-style discovery — Services and DNS are enough.
@HttpExchange("/orders")
public interface OrderClient {
@GetExchange("/{id}")
OrderResponse getById(@PathVariable long id);
}
Observability from day one
Without metrics, logs, and traces, microservices become a black box. Production baseline:
- Micrometer + Prometheus / OpenTelemetry metrics;
- structured JSON logs with
traceId/spanId; - distributed tracing (OTel → Jaeger/Tempo);
- Actuator health endpoints for readiness/liveness.
Takeaway
Java microservices work when boundaries are clear, contracts are versioned, and the platform (K8s, CI/CD, observability) is ready. Start simple: one service — one database, explicit APIs, idempotency, and measurable SLOs.
