Let’s make an exercise and imagine that you are about to develop a microservice. The first question pops out: Which functionality am I aiming to code ? It looks simple to answer, but lets think twice because maybe the first answer might make us blind.
Before start coding we need to identify properties, names, entities and interactions. I will write down here three principles that I’ve been successfully applying before start any project.
Services are autonomous
The english definition of autonomy is the condition of self-government or be independent.
Given the english definition, we are now able to apply it to service modelling: Services must have enough autonomy in order do their business capability without relying in other services.
If this principle is not met, you will see services becoming client of other services, making your service resources (threads, memory and CPU) dependents on the response of other services.
This becomes chaotic when there is a cascade of dependencies: “Service A” depends on “Service B” and “B” depends on “C”.
Services don’t share classes/types
It is important to keep your services protected and don’t share their classes or types, specially data storage types: tables/documents. If you allow it on your architecture, the autonomy principle is being misused and you end up having a hidden coupling between your services.
Have you ever changed a data type schema and accidentally broke a running code that you were never aware of the existence of that code, because you thought you were changing an “autonomous” service ? This situation gets worse with time, when your business keeps changing and you loose the dependency control over you services, because now resources are shared and hidden.
In essence shared resources hide high coupling, so be aware!
Instead of sharing resources with high coupling, make sure you explicitly model your dependencies through messages. I had the chance to write about events before, but another common message type is a command (used to request an action inside the boundaries of your service).
Services don’t entirely own an entity
If you asked yourself before: “Where do I put this entity ?” and was uncertain of the answer. Probably the best answer would be: “It depends!”
In fact, services own parts of an entity that makes sense to do their business capability.
Lets imagine that you developing an eCommerce that sells books and you need to model sales, payment and delivery processes. Then you decide to create an entity that will keep Order data: customer, products, quantities, prices, payment information and delivery address.
Everything works correctly, but now our eCommerce is a success and we decide to sell e-books as well. What to do with the delivery address when selling e-books ? Will the address be null, meaning that this order is about an e-book order ? 🤔
Adding a little bit more to our story, now we want to expand our payment methods and start accepting bitcoins, that is totally different from the conventional ways of payments. What will we do with the conventional payment method attributes ? Add another attribute for the bitcoin and leave the others null ? 🤔
The solution to these questions is split the domain of an Order into parts (services) where they belongs to and where they make sense to the business capability.
When it is necessary, the logistics service will react and do its business capability and delivery the order. The same way, billing system will know when and how charge a specific amount related to an order. Finally the sales service is worry in take care of the sales relationship with the customer.
Having these three principles in mind, you’ll notice that changing the business capability of your service will become a fast and testable activity, once all business capability is built-in on the autonomous, decoupled and cohesive service.
This article is also available in portuguese.