From Monolith to Microservices: A Small Team’s Journey with Spring Cloud & Kubernetes

This article chronicles a SaaS company's evolution from a two‑person monolithic Java application to a containerized microservice ecosystem using Spring Cloud, Kubernetes, Jenkins, and automated testing, highlighting practical decisions on API design, CI/CD pipelines, service splitting, and operational monitoring for small teams.

Java Backend Technology
Java Backend Technology
Java Backend Technology
From Monolith to Microservices: A Small Team’s Journey with Spring Cloud & Kubernetes

Is Microservice Architecture Suitable for Small Teams?

Microservices are a matter of perspective. As business complexity grows, monolithic applications become unwieldy, so splitting them into smaller services follows the divide‑and‑conquer principle. However, microservice architecture should evolve gradually; over‑design is risky.

The company provides SaaS services, custom development, and private deployments. In less than two years the architecture evolved from a monolith to microservices and finally to containerization.

Monolith Era

Initially only two developers worked on the project, making microservices unnecessary. The front‑end was built as a SPA with a clear separation from the back‑end, even though SEO was not a concern.

Front‑end and back‑end separation does not preclude server‑side rendering; a thin view layer (e.g., PHP or Thymeleaf) can be added when needed.

Deployment used Nginx to proxy static HTML and forward API requests to the back‑end on port 8080.

API Definition

Versioning: place version after /api/, e.g., /api/v2.

Resource‑centric URLs using plurals, e.g., /api/contacts or nested /api/groups/1/contacts/100.

Avoid verbs in URLs; naming consistency requires code review.

Supported actions: POST / PUT / DELETE / GET. Note that PUT is full update while PATCH is partial; the team treats PUT as partial by ignoring empty fields.

Swagger generates documentation for front‑end consumption.

Continuous Integration (CI)

Team members previously worked in large teams, so quality control and process management were important. Early on, integration tests targeting APIs (including DB reads/writes and MQ operations) were introduced, providing near‑production coverage without external service mocks.

Automated tests increased the need for data preparation and cleanup, especially when parallel tasks run.

Jenkins was adopted to automate the pipeline.

Developers submit code to Gerrit; Jenkins triggers compilation, runs integration tests, generates a report, and after passing, a reviewer performs a code review. This CI setup was sufficient for the monolith era.

Microservice Era

Service Splitting Principles

From a data perspective, services are split where database tables have few relationships—user management is a typical candidate. In DDD terms, a service groups related domain models, possibly with limited data redundancy to define boundaries.

Within a service, domain services coordinate multiple domain objects; however, DDD’s rich‑model approach can be challenging for many developers.

Splitting services is a large effort requiring business‑knowledgeable participants and consideration of team structure to avoid circular or bidirectional dependencies.

Framework Choice

The existing monolith used Spring Boot, so Spring Cloud was the natural choice for microservices. While language‑agnostic frameworks are ideal, both Dubbo and Spring Cloud introduce intrusiveness; integrating Node.js services into Spring Cloud revealed many issues, suggesting service mesh as a future direction.

Typical Spring Cloud components used:

Zuul as gateway to route client requests.

Eureka as service registry/discovery.

Hystrix for rate limiting and circuit breaking.

Feign + Ribbon for inter‑service calls, with Feign abstracting Eureka interactions.

Zuul’s synchronous model can consume many servlet threads under heavy I/O, leading to ~30% performance loss compared to direct calls. Spring Cloud Gateway (asynchronous) or Nginx‑based Kong are considered alternatives.

Architecture Refactoring

After six months and new requirements, the monolith was split into over ten microservices, accompanied by a Spark‑based BI system. Data sources expanded from MySQL to Elasticsearch and Hive.

Automated Deployment

Continuous Delivery (CD) was not fully realized due to resource constraints; instead, automated deployment was implemented.

Jenkins builds JARs, transfers them to a jump host, and Ansible deploys them to the cluster.

This straightforward approach works for small teams as long as testing (manual + automated) is thorough before deployment.

Link Tracing

Open‑source tracing tools like spring cloud sleuth + zipkin or Meituan’s CAT provide end‑to‑end request logs for performance analysis. The team uses a lightweight approach: logging RequestId (generated by the gateway) and TraceId (propagated through threads or message queues).

When a request triggers an MQ message, each consumer generates its own TraceId to differentiate processing paths.

Implementation uses ThreadLocal to store APIRequestContext within a service; on cross‑service calls the context is serialized into HTTP headers (or MQ headers) and reconstructed on the receiving side.

Operations Monitoring

Before containerization, the stack telegraf + influxdb + grafana collected JVM, system, and MySQL metrics, visualized via Grafana. spring boot actuator together with Jolokia exposed JVM endpoints—configuration‑only, no code changes required.

Containerization Era

Architecture Refactoring

Since containerization was planned from the start, each service now has a Dockerfile to build its image.

Key changes:

CI now builds Docker images.

Database upgrade tools are containerized and run separately from the application.

In production, Kubernetes services replace Eureka for service discovery.

Spring Cloud & k8s Integration

The team uses Red Hat OpenShift (enterprise k8s). A Kubernetes Service groups multiple Pods, providing built‑in load balancing similar to Spring Cloud Feign + Ribbon.

Because Spring Cloud’s ecosystem is intrusive for heterogeneous languages (e.g., Node.js BFFs), the team prefers native k8s service discovery over Eureka in production, while keeping Eureka for local development.

eureka.client.enabled = false   // disable Eureka registration
ribbon.eureka.enabled = false // prevent Ribbon from using Eureka
foo.ribbon.listofservers = http://foo:8080 // direct server address

CI Refactoring

CI now builds Docker images and pushes them to Harbor. Database migrations moved from Flyway (run on app startup) to a dedicated containerized tool executed once per service.

Concurrent upgrades caused long startup times due to Flyway’s DB lock; the new approach isolates upgrade tasks per service, running them as one‑off Docker containers (e.g., docker run --rm …) with optional target version parameters.

Jenkins pipelines also handle service dependency ordering (e.g., config, Eureka) to ensure correct deployment sequence.

Conclusion

Each topic could be expanded into a full article; microservice evolution touches development, testing, and operations, requiring close collaboration among diverse roles. Splitting large systems is essential, but small teams must adopt service‑oriented solutions thoughtfully, balancing architectural benefits against increased team demands.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ci/cdSpring Cloud
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.