Java EE 8 and Angular
上QQ阅读APP看书,第一时间看更新

Single responsibility

Your services should strive to achieve a single responsibility and not get tangled with many, which leads to more reasons for it to change than are necessary. This is one of the principles of SOLID design principles. The single responsibility principle is strictly speaking not about microservices, but instead about classes and objects and maybe too simplistic for it to be applied here. This principle, in the context of a microservice, results in each of the components being highly cohesive and staying focused around one business capability.

When organizing code, Java developers often use packages to create modules. In a layered architecture, modules are generally created by technical concerns, such as presentation, business, and persistence. Packaging by layers has certain disadvantages, such as changes to functionality usually requiring a change across all the layers involved. Another strategy to organise code is to package by business domain or features. This approach results in packages named tickets, users, and so on, which are created as feature modules. When modules are organised after the domain, it helps developers who are looking at the code structure build a quick understanding of what the application is about. It also tends to promote high cohesion and loose coupling between modules.

In microservice architecture, a service should focus on a single business capability. When building an issue management system for working with tickets that are assigned to users, we may choose to create a tickets microservice responsible for creating and fetching tickets. Similarly, we may also build a users microservice for creating and getting the users within the system. These services should be kept independent of one another and must have well-defined boundaries. It's not enough to keep the business logic separated; even the domain objects should be modeled to meet the purpose of a given microservice.

Typically, the domain entities are grouped as one big shared module (bundled as JAR), which gets added as a dependency to the service. The moment a tickets service fetches a Ticket entity (think JPA entity or a domain object), it inadvertently pulls in all related entities along with it. Assume a Ticket entity references a User entity and User references an Address entity; we would be polluting our tickets service with information it shouldn't get coupled with, such as an Address.

The idea of a bounded context is described by Eric Evans in his seminal book, Domain Driven Design. When crafting your domain model, you should identify the domain boundaries and align them to the business capability the service caters to. As you develop a model, you should identify its bounded context. Boundaries help prevent leaking of information in unwanted parts of your code. Going back to issue management system, both the users and tickets microservices may reference a Ticket object. Within the context of the users service, a Ticket object may have just the Id and Name properties, which are required. But the tickets service will require more details about the Ticket object, such as the status, dates, priority, and so on. So even though the Ticket entity may mean the same thing, it will be created as different classes, used by two different applications, with its own database:

This idea is very difficult to apply in a monolithic architecture, as most of the code base is collocated. But if you have a separate cross-functional team with its own database in a microservices architecture, this very same idea of bounded context is far more achievable. Even if you don't hit perfection, it's an idea worth trying.