Disclaimer: Parts of this post and the title image may have been generated using AI tools like ChatGPT and DALL-E 😉
Microservice architecture is a design pattern where a large application is split into smaller, independent services that talk with each other through some communication channel(s).
Benefits to using Microservice Architecture include, but are not limited to:
- Scalability: individual services can be scaled independently, allowing for more efficient resource allocation and faster response times.
- Resilience: if one services fails, the others can continue to function, resulting in increased system reliability and uptime.
- Flexibility: new features or changes can be added or made to individual services without affecting the entire system, making development and deployment faster and more agile.
- Independence: each services can be built using different programming languages or frameworks, have separate sets of dependencies and use different internal architecture and patterns, allowing teams to best adjust to their subdomain and problems.
- Maintainability: services should be small and therefore easier to understand and change or refactor, furthermore each service can be tested separately. Services can be deployed and updated separately, without disturbing other unrelated services.
There are also some problems specific to Microservice Architecture, like:
- Complexity: multiple independent services have to be all set up separately and communication between them must be properly coordinated.
- Debugging and Troubleshooting: the issues may lie in one of many services and it can be difficult to pinpoint the root cause.
- Inter-service communication: most of the business logic will not be contained in a single service, therefore services must have a way to communicate with each other over some transport protocol. Calls between services may result in increased latency compared to in-process communication in a monolithic architecture. Communication can be a point of strong coupling – something that contradicts the Microservice approach.
- Data consistency: updates to data may occur in multiple services simultaneously or change from one service should be acted upon by another one.
- Service discovery: services must be able to discover and communicate with each other in a dynamic environment where services (and their instances) may be added or removed frequently.
- Cost: without optimization the cost of microservice architecture infrastructure can be higher, microservice architecture can result in increased DevOps overhead.
How to organize and structure a Microservice Architecture project that will make it possible to use all the benefits while reducing scale of the problems it introduces? What tools can help solve the problems for us?
To overcome the issues connected to Microservice Architecture:
Complexity – templates for configuration of the entire services suite and each individual service should be introduced that would make them extensible when needed, but fast to deploy. Microservice Chassis and Service Template patterns can be used here.
Debugging and Troubleshooting – tracing, logging and monitoring should be considered from the start and hidden away as much as possible to not burden daily development.
Inter-service communication – communication channel should be chosen wisely and monitored to discover bottlenecks. To keep the services decoupled an Events Queue can be used to implement event based communication pattern. Tools like Traefik Proxy and Traefik Mesh can also help connecting the services directly if there is a need.
Data consistency – there are many possible approaches here, selected one must ensure that the services stay loosely coupled and atomic transactions spanning services are possible. Services should still be independent to use data storage solution best suited for their needs. Separate database per service with reads available for other services through API is one of the industry standards but brings its own issues that have to be addressed.
Service discovery – some kind of Service Registry must be introduced to allow services to reach each other, but communication protocols and logic should not be the consideration of the service itself. Can be solved with Service mesh and use of Traefik Proxy for discovery.
Cost – services should be monitored for performance and load to allow for optimization of their resources. To limit work on DevOps side the infrastructure should be clearly defined in code with use of tools like Terraform.
What is the best stack to do the job with Python?
We need an asynchronous and lightweight framework – 10Clouds usual choice of Django is a no-go for Microservice Architecture. Industry standard for async Python web framework is FastAPI and there is some experience with it already, so it seems to be a good match.
Highly opinionated list of tools, libraries and project that can be used for Microservices Architecture (with no reasoning whatsoever):
- Python backend API:
- Communication by events:
- Asynchronous server local tasks (could be also locally handled events):
- SQL Database where needed (some services will do fine without it):
- SQLAlchemy ORM
- PostgreSQL database
- Alembic for schema migration
- Tracing across services:
- OpenTelemetry as a standard instrumentation
- Jaeger collector for local development
- Proxy, service discovery:
- Traefik proxy for local development
- Build and deploy:
- Docker compose
- tilt to deploy full stack locally