Create your own
Lesson illustration

SRP for Microservice Boundaries

Hello! Welcome back to our course on preparing for microservices interviews.

In our last lesson, we explored how to use Domain-Driven Design (DDD) and the concept of a Bounded Context to perform an initial decomposition of a monolith. This gave us a strategic, business-focused way to draw the first lines in the sand and propose potential microservice boundaries.

Today, we'll take the next crucial step: refining and validating those boundaries. A first-pass design is rarely perfect. We need a way to scrutinize it to ensure our services are cohesive, independent, and genuinely "micro." To do this, we'll apply one of the most fundamental principles of software design: the Single Responsibility Principle (SRP). Understanding how to apply SRP at an architectural level is a hallmark of a senior engineer and a common topic in system design interviews.

By the end of this lesson, you will be able to apply SRP to evaluate proposed service boundaries, ensuring they are built around distinct business capabilities with a single reason to change.

1. Beyond Classes: SRP in Microservices

You're likely familiar with SRP from object-oriented programming: "a class should have only one reason to change." In microservices architecture, we elevate this principle. A microservice should be responsible for a single business capability and have only one reason to change.

But what does this really mean? The key is to think in terms of cohesion and coupling.

  • High Cohesion: Elements within a module (or microservice) are closely related and focused on a common purpose.
  • Low Coupling: Modules (or microservices) are independent of each other, and changes in one are less likely to impact others.

The ultimate goal of microservice decomposition is to achieve high cohesion within each service and low coupling between services. SRP is our primary tool for achieving high cohesion.

SOLID Principles? Nope, just Coupling and Cohesion

To frame this concept, let's watch a segment from the video 'SOLID Principles? Nope, just Coupling and Cohesion' by CodeOpinion. It does an excellent job of cutting through the jargon and focusing on what truly matters: designing services around business functions, not just data structures.

Watch from the beginning (00:11) to 03:17. Pay close attention to how the speaker defines cohesion in terms of business capabilities and directly links the Single Responsibility Principle to a 'narrow business function'.

As the video explains, a common mistake is creating services around data entities (e.g., a ProductService, a UserService). This often leads to low cohesion and high coupling. Imagine an e-commerce system where a single ProductService handles everything related to a product.

  • The Sales team needs it to manage pricing.
  • The Warehouse team needs it to manage inventory stock levels.
  • The Marketing team needs it to manage descriptions and images.

A change request from the Sales team (e.g., adding tiered pricing) would force a redeployment of a service that the Warehouse and Marketing teams depend on, increasing risk and coordination overhead. This service has multiple "reasons to change" because it serves multiple, distinct business functions. This is a classic violation of SRP at the service level.

2. The Litmus Test: Who is the "Actor"?

A powerful way to think about SRP, especially in interviews, is to ask: "Who is the actor responsible for this change?" An "actor" isn't a single user but a group of stakeholders, often a business department.

Single Responsibility Principle - A Real World Example in Go

This idea is clearly explained in the video 'Single Responsibility Principle - A Real World Example' by Kantan Coding. It frames SRP as being about organizing code to align with the people and departments who request changes.

Watch the first 90 seconds (00:00 - 01:30). The key insight here is that SRP merges the non-technical (company organization) with the technical (code structure).

If a single service needs to be changed in response to requests from both the Accounting department and the Customer Support department, it is serving two actors. This indicates it has more than one responsibility and is a candidate for being split.

3. A Practical Example: Identifying and Fixing an SRP Violation

Let's make this concrete. Imagine a food delivery application. An initial, naive design might put payment logic directly inside the Order Management Service.

13 Microservices Best Practices

The article '13 Microservices Best Practices' provides a simple and clear example of applying and violating SRP. Reading this will solidify your understanding of the real-world consequences.

Read the sections 'Example of Following the Single-Responsibility Principle' and 'Example of Violating the Single-Responsibility Principle'. Notice how violating SRP leads to increased team coordination and a higher risk of bugs.

The violation becomes obvious when you consider the actors:

  • The Operations team might request a change to how order statuses are tracked.
  • The Finance team might request a change to support a new payment gateway.

Because both sets of logic live in the same service, a change for the Finance team could accidentally break order tracking for the Operations team. The correct, SRP-compliant design is to split this into:

  1. An Order Management Service (responsible to the Operations team).
  2. A Payment Service (responsible to the Finance team).

Now, let's see how this plays out in code.

Single Responsibility Principle - A Real World Example in Go

The Kantan Coding video provides a fantastic, detailed walkthrough of identifying an SRP violation and refactoring the code. Although the example is in Go, the principles are universal and directly map to how you would structure a Spring Boot application.

Watch the following two segments: 01:30 - 04:24: This part explains the initial violation at both the service level (a Payment Service that also generates reports for Accounting) and the code level (a ReportFactory that contains database logic). 14:29 - 21:42: This part demonstrates the refactoring. Observe how the database logic is extracted from the ReportFactory into its own dedicated database package. In a Spring Boot context, this would be analogous to moving query logic from a domain service into a dedicated @Repository component.

The key takeaway from the refactoring is the separation of concerns. The ReportFactory's single responsibility is to create report objects. The responsibility of fetching data from the database belongs to a different component. This makes the system more modular, easier to test, and easier to change.

4. How "Micro" is Micro? Finding the Sweet Spot

Applying SRP can lead to a temptation to make services extremely small. However, services that are too granular (sometimes called "nano-services") can create a different problem: a "distributed monolith," where high chatty communication between services and complex deployment pipelines negate the benefits.

So, how do you find the right size? There are no hard rules, but there are useful heuristics.

Microservices — A Definitive Guide

The article 'Microservices — A Definitive Guide' offers excellent practical advice on sizing services. It provides both quantitative and qualitative guidelines to help you justify your design decisions in an interview.

Read the subsections under 'Defining ‘Micro’ in Microservices', focusing on: The Single Responsibility Principle (as a recap) Quantitative Metrics (e.g., lines of code, team size, API endpoints) Qualitative Indicators (e.g., business capability, data cohesion, team autonomy) Warning Signs of Services That Are Too Small Practical Recommendations

The qualitative indicators are the most important for architectural discussions. A service boundary is likely well-defined if:

  • It represents a distinct business capability.
  • The data it owns is highly cohesive (changes together).
  • A small, autonomous team can own it end-to-end (Conway's Law).

The following checklist is a fantastic tool for summarizing these factors and making a split decision.

Microservice Split Decision Checklist
This 'Microservice Split Decision Checklist' provides a practical framework for applying the principles we've discussed. In an interview, you could mentally walk through a checklist like this to structure your answer when asked whether to split a service.
Test your understanding!

Let's return to the conference management system from our previous lesson. The system was broken down into several Bounded Contexts, such as Orders and Registration and Pricing and Marketing.

Identifying Domain Models per Microservice or Bounded Context
A conceptual breakdown of the conference management system into distinct services.

Using the Single Responsibility Principle, explain why it's a good architectural decision to separate Pricing and Marketing from Orders and Registration. Who are the "actors" for each service, and what kind of changes might they request?

Show answer

This is a great example of applying SRP to validate Bounded Contexts.

  1. Orders and Registration Service:

    • Actor: The Event Operations or Registrar team.
    • Responsibility: Managing the lifecycle of an attendee's registration for a conference. This includes enforcing rules like registration deadlines, seat capacity, and waitlists.
    • Potential Changes: "We need to add a waitlist feature for sold-out sessions," or "We need to change the registration cancellation policy." These changes are related to the core operational logic of running the event.
  2. Pricing and Marketing Service:

    • Actor: The Marketing or Sales team.
    • Responsibility: Managing how conferences are priced and promoted to drive sales. This includes setting ticket prices, creating discount codes, and running promotional campaigns.
    • Potential Changes: "We want to run an 'early bird' pricing campaign for the first 100 tickets," or "We need to create a 20% discount code for our corporate partners." These changes are market-driven and can be frequent and experimental.

Why Separate?
Combining these two would violate SRP. The service would have two distinct actors with very different reasons for requesting changes. A marketer trying to launch a flash sale shouldn't have to worry about potentially breaking the core registration logic. By separating them, the Marketing team can iterate on pricing strategies independently and deploy changes without affecting the stable, operational Orders and Registration service. This leads to higher development velocity and lower risk.

Key Takeaways

  • SRP in microservices means a service should own a single business capability and have a single reason to change.
  • A "reason to change" corresponds to a request from a specific actor (e.g., a business department).
  • Applying SRP is the primary method for achieving high cohesion within your services.
  • Use SRP as a tool to validate and refine the Bounded Contexts you identified using DDD.
  • The goal is to find the "sweet spot" in service size. Use qualitative indicators like business capability, data cohesion, and team autonomy to avoid making services too large or too small.

Next Up

Now that we have a solid strategy for designing well-defined, independent services, our next lesson will focus on how to build them for the cloud. We'll explore the Twelve-Factor App methodology, a set of acclaimed best practices for creating scalable, resilient, and maintainable cloud-native applications.

Can't find a good explanation? Sign up and we'll make it for you

Sign up