π Understand the Domain: Use Domain-Driven Design (DDD)
Use Domain-Driven Design to identify Bounded Contexts β logically separate areas of the application that can become independent services. This is the most important step in microservices decomposition. For this you should understand the business domain. Spend some time in learning the domain before starting with this step. Any mistake here can be time consuming to change.
Example (E-commerce app):
- User Management
- Product Catalog
- Orders
- Payments
- Inventory
We have taken a domain that is know to all. From our understanding of this domain, we can easily select these candidates for microservices.
β Tip: If two parts of your system rarely change together or serve different business capabilities, they likely belong in different services.
Above tip is very important and helps a lot in brining clarity when in doubt. I have applied this in practical scenarios and found to be very useful.
π§±Β Analysis Phase – Analyze Modules and Responsibilities
Look at the existing codebase and break down the modules, classes, or packages by their functionality.
- The existing code can tell a lot of things. Check folder structure, services, and data access layers. If these are well organised, then the job becomes a lot easier.
- Look at code ownership β who changes what the most? – Another way to find out what gets changes the most and which department is responsible for the changes. Within the IT team itself , there will be developers maintaining different portions of the business service.
- Group related business logic. – First note it down in a draft document, then discuss with the existing team , validate it and then finalize the grouping.
πΒ Define the Service Boundaries
A microservice should:
- Own its data
- Should be independently deployable
- Serve a single business capability
Aim for a cohesive, autonomous unit and avoid splitting too granularly. Too much granular service will reduce the benefits of microservices and instead bring in too much complexity, maintenance night mare.
Example:
Instead of creating a UserImageService, UserInfoService, and UserActivationService, just have a User Service.
π Design APIs and Communication
Design APIs (REST/gRPC/message queues) for inter-service communication. Define:
- Request/response models
- Event schemas (if using event-driven architecture)
π‘ Use API Gateways or Service Mesh for managing communication, versioning, and routing.
π Split the Database
Each microservice should own its data. There should be a separate database for each microservice , avoid a shared database.
Different Approaches:
- Extract relevant tables into new schemas – For example all order related table can go into a say orders and user related can go into profile schema.
- Use replication or event streaming (e.g., Change Data Capture) – This can be used to sync different systems that may need this data.
- When the services are split into microservices, there will be cases where multiple services needs to be invoked to fullfil a business flow. Hence use eventual consistency with messaging (Kafka, RabbitMQ) to make the data consistent across various services.
π Refactor and Extract Incrementally
Adopt the Strangler Fig Pattern:
- Create new microservices alongside the monolith
- Route traffic gradually to new services
- Retire parts of the monolith once functionality is fully moved
π§ͺ Test and Monitor
- Set up contract testing between services (e.g., Pact) .Contract testing will ensure that the client and the server and in loop when making api changes. This will make the API more robust and stable.
- Add observability: logging, metrics, tracing (e.g., Jaeger, Prometheus). You will also need to identity tools for this. Grafana and similar tools are a good choice for this.
- Use feature flags to control rollouts. Enable / disable feature flags during migration and transition phase to selectively make available services / business functions/
π Automate Deployments and CI/CD
Automation is the key to Microservices. Use
- CI for testing
- CD for independent deployments
- Containerization (Docker) and orchestration (Kubernetes) for rapid and scalable deployments.
π Key Principles to Follow
Principle | Description |
Single Responsibility | Each service should do one thing well |
Independent Deployment | Services should be deployable and scalable independently |
Own Your Data | Each service must own its persistence |
Communication Contracts | API or message formats should be well-defined |
Domain-Centric Boundaries | Reflect business domains, not just technical layers |
β
Example Decomposition β Courier Application
Monolith Module | Microservice Candidate | Notes |
---|---|---|
Auth Controller | Auth Service | JWT/OAuth, user sessions |
Delivery Controller | Delivery Service | CRUD for delivery related activities |
Feedback Controller | Feedback Service | User feedback , replies |
Search Logic | Search Service | For finding the courier bookings by id, customer id etc. |
Analytics Module | Analytics Service | A view into what goes in the system |
π Tooling to Help the Process
- Code Analysis: SonarQube, PMD, CheckStyle
- Refactoring Tools: Tools in IDEs, OpenRewrite, jQAssistant
- Service Extraction: Micronaut, Spring Boot, Spring Webflux, .NET Minimal APIs
It is not an easy task to decide what should be grouped together into a single service and what should be split into a seperate service. Understanding of the domain goes a big way in acheiving this. Start with many examples before doing it in a real world project.
Leave a Reply