💡
Microservices are a design pattern in software architecture that aims to shorten communication paths through smaller teams and more focussed responsibilities, thereby reducing time-to-market. The basic ideas behind microservices go back to the Unix principles formulated by Doug McIlroy¹ in 1978. According to him, programmes should be designed in such a way that they …

... have exactly one responsibility and fulfill it well,
... can be nested one after the other so that one program produces the input for another program,
... can be tested early and abandoned if necessary,
... utilize tools for recurring tasks during development.
Characteristics of microservices

Microservices² are being characterized by the following properties:

  • Single Purpose: As with Unix principles, a microservice should fulfill exactly one task well.
  • Encapsulation: Microservices have sole ownership of their data. They interact with the outside world via well-defined interfaces.
  • Ownership: A single team (ideally consisting of 5-9 people) is responsible for a microservice over its entire lifetime.
  • Autonomy: The team responsible for the microservice can build and deploy the microservice at any time without consultation of other stakeholders. The team is free to make implementation decisions.
  • Multiple versions: It is possible for different versions of a microservice to exist at the same time.
  • Choreography: There is no centralized system that orchestrates a workflow. Instead, each microservice is able to independently provide itself with the information it needs for its functionality.
  • Eventual consistency: A temporary inconsistency of data between microservices is accepted as long as the data eventually becomes consistent again.


Challenges of microservices

Due to their internal simplicity, microservices are generally more scalable and easier and quicker to change than monoliths, in which the entire logic is contained in a single program. Due to the small teams, microservice teams feel (and are) much more responsible for the success of their microservices, which often leads to better results and decisions. Microservices facilitate omnichannel solutions through shared backend functionality that can be consumed by different user interfaces.

Increased complexity

At the level of individual microservices, we create a simple and beautiful world through the limited scope of tasks and encapsulation. The inner simplicity of microservices comes at the price of increased complexity in terms of communication between microservices and a certain degree of redundancy: as the microservice teams are fully responsible for their services (ownership) and can also deploy them independently (autonomy), the teams must be technically capable of actually carrying out this deployment. This includes both cloud infrastructure expertise and the resources required to operate the infrastructure.

In more concrete terms, we can imagine a mailing service that is responsible for sending informational emails about a product to customers. The service must be built during development and then executed. The computers used for this must have the necessary connectivity to perform these tasks. Once the service is running, other services must be able to initiate the email process. Possible ways of doing this include an event system from which the mailing service independently filters out the events relevant to it (in the sense of the choreography property) and processes them into emails. 

However, such an event system or messaging system must firstly be provided and secondly be usable by different teams and their respective microservices. Another conceivable scenario would be to address the mailing service via a REST interface. In this case, the client must be able to communicate with the mailing service via a network and this connection must be secured so that only authorized services have access to the interface.

Authentication & authorization

Authentication and authorization is typically a relatively complex problem and should also be handled consistently within the company to increase traceability. It is therefore recommended that microservice teams are not responsible for this issue independently. 

As several instances of the mailing service can coexist in different versions (multiple versions) and microservice instances can be redeployed and, in particular, stopped at any time (autonomy), it is essential that relevant data (such as completed mails that have not yet been sent) is stored permanently across all instances. This requires databases and persistent volumes that need to be provided and maintained. Backups and snapshots of the data should also exist and be tested regularly.

Errors pose particular challenges: if any process in the overall system suddenly stops working, it is important to be able to analyze errors across multiple microservices. A consistent logging infrastructure as well as request tracing and alerting are helpful for this. Of course, infrastructure must also be provided for these solutions.

Summary

The challenges described show that there are a number of points where we should ensure standardization across services. We will take a closer look at some of these points in the next article. Elements of the software architecture that are (potentially) relevant for several building blocks of the architecture (in our specific case, microservices) are called cross-cutting concerns. To avoid redundancy, such cross-cutting concerns should be dealt with in the overarching architecture documentation, regardless of the individual components. It is important to define cross-cutting concerns at an early stage, as a migration at a later date will take up a lot of capacity across all teams.

💡
Coming up
In the second post, we look at the basic building blocks of microservices, which differ only slightly and can therefore be managed centrally by an infrastructure team. 
Resources:
¹ Doug McIlroy - https://archive.org/details/bstj57-6-1899/mode/2up
² Kelly Goetsch - https://commercetools.com/resources/booklet/microservices