How granular should a component be?
Simply put, if aspects of your component—like changes, failures, or scaling—can occur together, it should remain a single unit. However, the moment it requires independent alterations, failure management, or scaling, it’s time to consider breaking it down into separate services.
In his manufacturing process improvement book The Goal (North River Press), Eliyahu Goldratt observes that “technology can bring benefits if, and only if, it diminishes a limitation.”
This is analogous to our cloud native approach, where our aim is to streamline the process of building and operating applications. Hence, the only rationale for dividing a component into smaller entities should be to overcome a limitation:
- When a component doesn’t yet need to change, fail, or scale independently, it is fine to keep them as modules in a modular monolith.
- When separate teams come across limitations due to sharing the code, it makes sense to split components out.
- When a piece of functionality is limited on how much it can scale, it makes sense to split it out.
Remember, the more you break a system down into fine-grained components, the more complexity you introduce. While automation and observability can mitigate this complexity to an extent, it’s crucial to always be mindful of the reasons for downsizing a component and to consider the trade-offs involved.
Leveraging Domain-Driven Design for Defining Microservice Boundaries
In cloud native system design, one key consideration is the delineation of boundaries between microservices. Each microservice should ideally have a single responsibility, providing it with only one reason to change, thereby promoting a clear separation of concerns.
Domain-driven design (DDD), a technique centered around domain knowledge, plays a vital role in this context. When implemented in a microservices environment, DDD can enhance the system’s design and maintainability by establishing a laser focus on the business domain and enforcing a clear demarcation of responsibilities among the various microservices.
Implementing DDD within microservices offers several advantages:
Domain-driven architecture
DDD facilitates the identification and modeling of the core business domains within a system. These domains subsequently guide the microservices’ design, leading to an architecture that’s intuitively aligned with business requirements and exhibits logical consistency.
Bounded contexts
A crucial principle of DDD is defining clear boundaries—termed bounded contexts—for each microservice. Identified by the specific business capabilities they represent, these boundaries ensure that each microservice has a distinct scope and purpose, simplifying their understanding and maintenance.
Domain language
DDD promotes the use of a shared domain language, enhancing communication and collaboration within the development team. In a microservices environment, where different teams might handle different microservices, this is particularly important.
Evolvability
DDD supports the concept of continuous evolution and improvement, perfectly aligning with a microservices environment where the system often evolves in tandem with the business and incorporates new features over time. By maintaining focus on the business domain, DDD ensures that any changes align with the system’s overall goals and objectives.
Domain-driven design is an expansive subject with a vibrant community and dedicated conferences. Numerous resources are available for learning its concepts and practices. Domain-Driven Design by Eric Evans (Addison-Wesley) serves as the definitive reference. However, Learning Domain-Driven Design by Vlad Khononov (O’Reilly) serves as an accessible starting point for newcomers.