Phase 08 — Distributed Systems, Applied Integration & Java for ML

This is the final vertical. Phase 07 taught you to ship one service; Phase 08 teaches you to make many services collaborate — reliably, asynchronously, and at scale — and to bolt an ML model onto that stack without pretending Java is Python. By the end you will have a microservice system on Kubernetes, an event pipeline on Kafka, and a Spring Boot service that serves an ONNX classifier. That is the M13 portfolio anchor.

Distributed systems are famous for failing in ways you couldn’t predict. This phase is deliberately blunt about the trade-offs: what actually works in enterprise Java, what looks good in slide decks and dies in production, and where the honest engineering judgement lives.

Timeline

Months 11–13 (Weeks 45–56, ~120–150 hours). This is a shorter phase by wall-clock but denser by concept. Do not skim.

Why This Phase Exists

Most Java engineers can build one Spring Boot service. Fewer can integrate five services without introducing bugs that only appear at 3 a.m. under load. Even fewer can serve an ML model from the JVM without either (a) shelling out to Python and calling it a day, or (b) reinventing Triton badly. This phase closes those two gaps, in that order.

What You’ll Learn

#

Topic

File

Focus

01

Microservices patterns

01_microservices_patterns.md

Decomposition, API gateway, Resilience4j, correlation, idempotency

02

Event-driven with Kafka

02_event_driven_kafka.md

Spring Kafka, delivery semantics, Streams, Debezium, alternatives

03

Caching & distributed state

03_caching_and_state.md

Redis, cache-aside, Redisson locks, stampede prevention

04

Java for ML/AI applications

04_java_for_ml_ai_applications.md

DJL, Tribuo, ONNX Runtime, Spring AI, LangChain4j

05

Serving ML models from Java

05_serving_ml_models_from_java.md

ONNX loading, Triton gRPC bridge, batching, drift monitoring

06

Cloud-native Kubernetes

06_cloud_native_kubernetes.md

Probes, HPA, sidecars, Helm, ArgoCD, Native trade-offs

07

Case studies & war stories

07_case_studies_and_war_stories.md

Netflix, LinkedIn, Twitter, ING, Uber Michelangelo

Projects & capstone

projects.md

ML categorizer, Kafka pipeline, capstone microservice system

How to Work Through This Phase

  1. Read files 01–03 first. These are the load-bearing distributed patterns.

  2. Then 04 and 05 together — the Java-ML story only makes sense as a pair.

  3. File 06 is a consolidation of Kubernetes-shaped ideas you’ve already touched in Phase 07/07.

  4. File 07 is deliberately last. War stories only land when you’ve felt the pain in files 01–06.

  5. Start Project (a) after finishing file 05. Start Project (b) after file 02. The capstone (c) needs everything.

Expect to reread files 01 and 02 at least twice. Distributed systems concepts don’t stick on first pass.

Dev Environment

Add to your Phase 07 setup:

# macOS / Linux
brew install kubectl helm kind
brew install --cask docker    # or OrbStack; you already have this
brew install kafka             # optional; docker-compose is fine
pip install torch onnx onnxruntime   # for producing ONNX artefacts to serve from Java

You’ll also want:

  • kind or minikube for a local Kubernetes cluster (kind is faster to boot)

  • k9s for a sane Kubernetes UI in the terminal

  • stern for tailing logs across pods

  • A free Confluent Cloud or Aiven Kafka trial (optional; local Kafka in Docker Compose is fine)

  • An Ollama install (brew install ollama) if you want to run local LLMs for the Spring AI / LangChain4j sections

Exit Criteria

You can call Phase 08 done when you can honestly tick every box:

  • Decompose a monolith spec into 3–5 services and defend the seams (DDD-lite bounded contexts, not “one service per table”)

  • Configure Resilience4j circuit breaker + retry + bulkhead + timeout on a Spring Boot client and explain each parameter

  • Explain Kafka producer delivery semantics (at-most-once, at-least-once, exactly-once) and which config flags produce each

  • Wire a Spring Kafka consumer with manual ack + retry topic + dead-letter topic

  • Pick between Kafka, Pulsar, RabbitMQ, and NATS for a given scenario with justification

  • Implement cache-aside with Spring Data Redis and defend against cache stampede

  • Load an ONNX model from a Spring Boot service and expose it as a REST endpoint with latency < 100ms p95

  • Explain when to serve a model from Java directly vs proxy to a Triton/TorchServe backend

  • Give the honest Spring AI vs LangChain4j verdict without cheerleading either

  • Deploy a multi-service system to a local Kubernetes cluster (kind/minikube) with Helm, probes, HPA, and observability

  • Ship the capstone: URL shortener extended into a microservice system with Kafka + Redis + JWT + Prometheus/Grafana on K8s

What Most People Get Wrong

  • Cargo-cult microservices. They split a small app into 12 services on day one, then spend 6 months building infra that a monolith wouldn’t have needed. A monolith with clean modules is usually the right answer until it isn’t.

  • “Exactly-once” fetishism. Real exactly-once in Kafka needs idempotent producers, transactional writes, and consumer-side idempotency. Most teams need at-least-once + idempotent handlers, which is simpler and enough.

  • Reinventing Triton. Loading ONNX in Java is fine for classical models. For LLMs and heavy transformers, put Triton/TorchServe behind a gRPC call and stop pretending the JVM is a research platform.

  • Ignoring failure modes until they happen. Circuit breakers, retries, and timeouts are configured last “when we have time.” They never do. Add them in the first commit.

  • Kubernetes because everyone else is. If you’re running two services with fewer than 100 QPS, ECS Fargate or Cloud Run will save you three months of yak-shaving.

Don’t be that engineer.


Previous phase: ../07_enterprise_spring_data/README.md · Next: 01_microservices_patterns.md