- Java Microservices Tutorial
- Java Microservices - Home
- Microservices - Introduction
- Microservices vs Monolith vs SOA
- Java Microservices - Environment Setup
- Java Microservices - Advantages of Spring Boot
- Java Microservices - Design Patterns
- Java Microservices - Domain Driven Design
- Java Microservices - Decomposition by Business Capability
- Java Microservices - Decomposition by Subdomain
- Java Microservices - Backend for Frontend
- Java Microservices - The Strangler Pattern
- Java Microservices - Synchronous Communication
- Java Microservices - Asynchronous Communication
- Java Microservices - Saga Pattern
- Java Microservices - Centralized Logging (ELK Stack)
- Java Microservices - Event Sourcing
- Java Microservices - CQRS Pattern
- Java Microservices - Sidecar Pattern
- Java Microservices - Service Mesh Pattern
- Java Microservices - Circuit Breaker Pattern
- Java Microservices - Distributed Tracing
- Java Microservices - Control Loop Pattern
- Java Microservices - Database Per Service
- Java Microservices - Bulkhead Pattern
- Java Microservices - Health Check API
- Java Microservices - Retry Pattern
- Java Microservices - Fallback Pattern
- Java Microservices Useful Resources
- Java Microservices Quick Guide
- Java Microservices Useful Resources
- Java Microservices Discussion
Java Microservices - Decomposition by Subdomain
Introduction
Modern software systems must evolve quickly, scale independently, and remain robust in the face of change. Microservices architecture provides a foundation for these requirements by breaking down applications into independent services.
However, how we decompose a system is critical. A poor decomposition can lead to tight coupling, poor scalability, and development friction. Among the various decomposition strategies, "Decomposition by Subdomain" − driven by Domain-Driven Design (DDD) − stands out as one of the most effective and sustainable methods.
This article explores the Decomposition by Subdomain pattern in microservices, its rationale, implementation approach, and real-world applications using Spring Boot.
What is Decomposition by Subdomain?
Definition
Decomposition by subdomain is a microservices design pattern that breaks a system into services based on domain substructures called subdomains, identified through Domain-Driven Design (DDD).
Instead of organizing services by technical functions (like DAO, controllers), we organize them by business function areas such as−
Customer Management
Billing
Inventory
Shipping
Each subdomain becomes a bounded context, which maps directly to a microservice.
Benefits of Decomposition by Subdomain
| Sr.No. | Benefit | Explanation |
|---|---|---|
| 1 | High Cohesion | Services handle a specific, focused domain task |
| 2 | Loosely Coupled Services | Minimizes dependencies between services |
| 3 | Aligned to Business Goals | Improves communication between technical and business teams |
| 4 | Supports Team Autonomy | Teams can own and evolve services independently |
| 5 | Easier Maintenance | Smaller, focused services are easier to debug and test |
Identifying Subdomains: A Case Study
Let's consider an online learning platform like Coursera.
Business Capabilities
User Registration
Course Catalog
Enrollment & Payment
Content Delivery
Certification
Decomposed Subdomains
| Sr.No. | Subdomain | Microservice |
|---|---|---|
| 1 | Identity & Access | auth-service |
| 2 | Course Management | course-service |
| 3 | Payment & Enrollment | enrollment-service |
| 4 | Video Streaming | streaming-service |
| 5 | Certificate Issuance | certification-service |
Implementing the Pattern Using Spring Boot
We'll illustrate with two subdomains: Course Management and Enrollment.
Course-Service (Core Subdomain)
Responsibilities
Manage course creation, categories, metadata.
CourseController.java
@RestController
@RequestMapping("/courses")
public class CourseController {
@GetMapping("/{id}")
public String getCourse(@PathVariable String id) {
return "Course info for ID: " + id;
}
@PostMapping("/")
public String createCourse(@RequestBody Course course) {
return "Course created: " + course.getTitle();
}
}
application.yml
spring:
application:
name: course-service
server:
port: 8081
Enrollment-Service (Core Subdomain)
Responsibilities
Manage student enrollment and payment status.
EnrollmentController.java
@RestController
@RequestMapping("/enrollments")
public class EnrollmentController {
@PostMapping("/")
public String enroll(@RequestBody Enrollment enrollment) {
return "Student enrolled in course ID: " + enrollment.getCourseId();
}
}
application.yml
spring:
application:
name: enrollment-service
server:
port: 8082
Manage student enrollment and payment status.
Each service has −
Its own data model
Database
And communicates via REST or asynchronous events.
Communicating Across Subdomains
Subdomain-based services often need to interact.
REST Call (Synchronous)
enrollment-service calls course-service to validate a course −
@Autowired
private RestTemplate restTemplate;
public String getCourse(String id) {
return restTemplate.getForObject("http://course-service/courses/" + id, String.class);
}
Event-Driven (Asynchronous)
Using Kafka or RabbitMQ for loose coupling −
course-service emits CourseCreatedEvent.
enrollment-service listens and updates its cache.
Aligning Subdomains with Bounded Contexts
Subdomain decomposition often aligns with bounded contexts in DDD.
Bounded Context − A logical boundary where a particular domain model is defined and applicable.
This allows −
Unique data models
Different vocabularies
Clear API boundaries
Example
course-service uses CourseEntity
enrollment-service uses CourseView (DTO)
This prevents leaky abstractions and supports data autonomy.
Subdomain Database Design
Each service/subdomain must own its data.
Microservice DB Ownership
| Sr.No. | Service | Tables |
|---|---|---|
| 1 | course-service | courses, categories |
| 2 | enrolment-service | enrolments, students |
| 3 | auth-service | users, roles, permissions |
No shared schemas or cross-database joins.
For queries across services: use data replication, event-driven updates, or API composition.
Best Practices and Considerations
| Sr.No. | Best Practice | Tables |
|---|---|---|
| 1 | Use domain modeling | Deeply understand the business language |
| 2 | Keep bounded contexts separate | Avoid accidental coupling |
| 3 | Implement shared contracts | Use OpenAPI or shared message formats |
| 4 | Ensure services work together | Use Event Storming or DDD modeling |
| 5 | Use observability tools | Monitor interactions (e.g., Sleuth, Zipkin, Prometheus) |
Real-World Example: Netflix
Netflix decomposes by subdomain−
| Sr.No. | Subdomain | Service Name |
|---|---|---|
| 1 | Playback | video-stream-service |
| 2 | Recommendation | reco-engine-service |
| 3 | Account Management | account-service |
| 4 | Billing | billing-service |
Each team owns one or more subdomains and releases features independently.
Challenges and How to Address Them
| Sr.No. | Challenge | Solution |
|---|---|---|
| 1 | Data consistency | Use eventual consistency + sagas or event sourcing |
| 2 | Duplication of logic/data | Keep services independent, use APIs to sync |
| 3 | Complexity of orchestration | Use orchestration (e.g., Netflix Conductor) or choreography |
| 4 | Domain boundaries unclear | Use Event Storming or DDD modeling |
Conclusion
Decomposition by Subdomain is a powerful pattern that promotes −
Business-aligned services
Autonomous development teams
Scalable and maintainable architecture
It fosters long-term agility by structuring software based on what the business actually does, not just on technology or project constraints.
With proper modeling, tooling, and communication strategies, subdomain decomposition leads to systems that are easier to build, grow, and maintain.