Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 53

Monolith to Microservice: Extracting a Spring Boot Service

Recommended resources

Hello!

In our last module, we put the finishing touches on our application's security, culminating in a complete configuration for stateless, token-based authentication. Our application, while secure, is still a monolith—a single, unified codebase. Now, we begin the exciting process of breaking it apart.

This lesson marks the start of our "Building and Integrating Microservices" module. Our goal is to refactor a monolithic application by creating a new, independent Spring Boot microservice. We will take a piece of our existing application, extract it into its own separate project, and refactor it to stand on its own.

Given your extensive experience with system design and microservice architectures, we will bypass the introductory "what" and "why" of microservices. Instead, we'll focus on the practical "how"—the specific patterns and Spring Boot mechanics for incrementally decomposing a monolith.

1. The Migration Strategy: From Monolith to Microservices

A "big bang" rewrite, where development on the existing system halts in favor of building a new one from scratch, is fraught with risk. A more robust and widely adopted approach is incremental migration. Let's quickly review the core strategy.

Monolith to Microservices Migration | Microservices Tutorial

This video from Selenium Express provides a concise overview of the strategic thinking behind a microservice migration. It discusses why the 'Big Bang' approach often fails and introduces a better, incremental strategy.

First, watch the section from 02:00 to 06:10, which explains the pitfalls of the 'Big Bang' approach. Then, continue from 06:36 to 10:49, which discusses how to strategically identify the first microservice to extract based on factors like frequency of change and resource needs.

The key takeaway is to migrate one piece at a time. This allows the monolith to remain operational while new services are gradually carved out and deployed. This process is often guided by the Strangler Fig Pattern.

The Strangler Fig Pattern, named after a type of vine that grows around a host tree and eventually replaces it, is a powerful architectural pattern for incremental refactoring.

Strangler Fig Pattern for Monolith to Microservice Refactoring
This diagram illustrates the three main steps of the Strangler Fig pattern: 1) A new, independent service is created. 2) A proxy or gateway begins routing 'read' requests to the new service. 3) 'Write' requests are also routed to the new service, and the old functionality is eventually removed from the monolith.

The following video section and a brief reading from a handbook will provide a solid definition of this crucial pattern.

Monolith to Microservices Migration | Microservices Tutorial

This segment of the Selenium Express video gives a clear explanation of the Strangler Fig pattern, using diagrams to show how a proxy routes traffic to newly created microservices over time.

Watch from 12:05 to 17:59. Focus on how the 'Strangler Facade' (an API Gateway or proxy) intercepts requests and selectively routes them to either the monolith or the new microservice.

[PDF] Transitioning from Monolith to Microservices Handbook

For a textual definition, this excerpt from the 'Transitioning from Monolith to Microservices Handbook' describes the Strangler Fig and another related pattern, the Anticorruption Layer.

On page 44, read section 4.4.9, 'The strangler fig pattern'. This will reinforce the concept from the video.

Our goal for today is to take the first practical step in this pattern: extracting a module into a new, independent service.

Refactoring Monolith to Microservices: Extracting Delivery Service
This diagram shows our goal. The 'AS-IS' architecture is a monolith. The 'TO-BE' architecture features a new, independent 'Delivery Service' with its own database, while the original monolith shrinks. An API Gateway directs traffic accordingly.

2. The Practice: Extracting Microservices from a Monolith

We will now walk through the process of taking a hypothetical monolithic application containing Company, Job, and Review functionalities and extracting each into its own Spring Boot microservice.

The following video is a comprehensive, step-by-step guide that mirrors exactly what we need to learn. We will go through it section by section, starting with the Job service.

Converting Job App to Individual Microservices | Spring Boot REST API to Microservices | Video #7

The video 'Converting Job App to Individual Microservices' by EmbarkX is an excellent hands-on tutorial. It demonstrates the entire refactoring process, from creating new projects to decoupling code and configuring the services.

Watch the section from 04:40 to 18:23. The presenter will guide you through creating the Job microservice. Pay close attention to how the Job entity is modified to break its direct dependency on the Company entity.

Key Refactoring Steps for the Job Microservice

As you saw in the video, the process involves more than just copying code. The most critical step is decoupling.

  1. New Project Creation: A new Spring Boot project (job-ms) is created using Spring Initializr with the necessary dependencies (Web, JPA, H2/PostgreSQL).

  2. Code Migration: The job package (containing the controller, service, repository, and entity) is copied from the monolith to the new project.

  3. Decoupling the Entity: This is the core of the refactoring. In the monolith, the Job entity likely had a direct object relationship with the Company entity:

    // In the monolith's Job.java
    @ManyToOne
    private Company company;
    

    This creates a hard dependency. The new Job microservice doesn't have a Company class or table. To break this dependency, we replace the object reference with a simple ID:

    // In the new Job microservice's Job.java
    private Long companyId; 
    

    This change embodies the principle of each microservice owning its own data and communicating via identifiers, not shared object models.

  4. Independent Configuration: The application.properties file in the job-ms project is configured with its own unique server.port (e.g., 8082) and its own database connection. This ensures it can run as a completely separate process.


3. Reinforcement: Extracting the Company Microservice

Now, let's reinforce the pattern by observing the extraction of the Company microservice. The process is similar, but it further illustrates how dependencies are removed.

Converting Job App to Individual Microservices | Spring Boot REST API to Microservices | Video #7

Let's continue with the same video to see how the 'Company' microservice is created.

Watch from 18:23 to 28:21. Notice that the Company entity in the monolith had @OneToMany relationships with Job and Review. These are removed in the new Company microservice to ensure its independence.

By extracting both Job and Company services, we've established two independent services that previously had a direct, coupled relationship.


4. Handling API Design in Decoupled Services

Extracting the Review service introduces a new challenge related to API design. In a monolith, it's common to have nested URLs that reflect entity relationships, like GET /companies/{companyId}/reviews. This URL structure implies that reviews are a sub-resource of a company.

For an independent Review microservice, this URL is problematic because the service should not be bound to the companies path.

Converting Job App to Individual Microservices | Spring Boot REST API to Microservices | Video #7

This final extraction demonstrates how to refactor API endpoints to suit a microservices architecture.

Watch from 28:21 to 57:12. This is a longer segment, but it's very important. Focus on the explanation around 42:45, where the presenter discusses redesigning the URLs. Note the shift from using a path variable for companyId to using a request parameter.

API Refactoring Explained

The key change is in how the ReviewController endpoints are defined:

  • Monolithic-style URL:
    GET /companies/{companyId}/reviews
    Here, companyId is a @PathVariable. This URL structure tightly couples the Review resource to the Company resource's path.

  • Microservice-style URL:
    GET /reviews?companyId={companyId}
    Here, companyId is a @RequestParam. The endpoint belongs solely to the Review microservice's domain (/reviews). It can filter by companyId, but its identity is not dependent on the company path.

This is a subtle but fundamental aspect of designing clean, loosely-coupled microservice APIs.


5. A Practical Tip for Your Workflow

When working with multiple microservices, constantly switching between different IDE windows can be cumbersome. The video offers a helpful tip for managing all your services within a single IntelliJ IDEA window.

Converting Job App to Individual Microservices | Spring Boot REST API to Microservices | Video #7

This brief section shows how to set up your IntelliJ workspace to manage all three new microservices together.

Watch from 57:12 to 1:03:43. This demonstrates how to add multiple Maven projects to one window and manage them via the 'Services' tab.


Conclusion

In this lesson, we took our first major step into building microservices with Spring Boot. We moved from theory to practice, applying the Strangler Fig pattern to extract three distinct services from a conceptual monolith.

Key Takeaways:

  • Incremental Refactoring: We broke down the monolith piece by piece, which is a safer and more manageable approach than a complete rewrite.
  • Decoupling is Paramount: The core of the refactoring involved breaking direct dependencies.
    • Data Decoupling: We replaced direct entity relationships (e.g., @ManyToOne) with simple ID fields (Long companyId). Each service should own its data.
    • API Decoupling: We redesigned API endpoints (e.g., from /{companyId}/reviews to /reviews?companyId=...) to make each service's API independent and self-contained.
  • Independent Configuration: Each new microservice is a full-fledged Spring Boot application with its own server port and configuration, allowing it to be developed, deployed, and scaled independently.

Now that we have separate, independent services, a new question arises: how do they talk to each other? For instance, how can we get the details of a Company when we only have its ID in the Job service?

In the next lesson, we will begin to answer that. We will explore inter-service communication, starting with synchronous, blocking calls using Spring's RestTemplate.