When using a microservice architecture, the most straightforward way to organise code is to have a single repository per microservice. However, that leads to a lot of repeated boilerplate and yak shaving when improvements to repo structure have to be introduced to all services (for example, a CI pipeline overhaul). Cascading pull requests are even more of a nuisance, if some of the services share dependencies.
Why are we using microservices in the first place, though?
To achieve independent scalability of the deployable artifacts - but these artifacts don't live in the source code repository anymore, they're executables!
Could we have the benefits of both - a single repository with low maintenance overhead, yet still keep the deployment flexibility and scalability of independent services?
Let's discuss the monorepo pattern in Elixir, its pros, cons, and practical considerations.
4. “A system design copies the communication structure
of the organisation building it”
Melvin E. Conway, 1967
5.
6.
7. Two-pizza team
- 3-9 people, “can be fed with two pizzas”
- Engaged
- Autonomous
- Owns the product over its entire lifecycle
- 1:1 relationship - one team maintains a single microservice
- “A high performance development organization consists of multiple teams”
Sources:
1. Learn to Scale Innovation, Not
Complexity
2. Service per team
8. Microservice
Benefits:
- Modularity
- Scalability
- Integration of heterogeneous and legacy systems
- Distributed development
Concerns:
- Services form information barriers
- Inter-service calls over a network have a higher cost
- Testing and deployment are more complicated
- Moving responsibilities between services is more difficult
- Viewing the size of services as the primary structuring mechanism can lead to too many services
- Two-phased commits are regarded as an anti-pattern
- Development and support of many services are more challenging if they are built with different tools and technologies
- The protocol typically used with microservices (HTTP) was designed for public-facing services, and as such is unsuitable for working
internal microservices that often must be impeccably reliable
- Decomposition methodology often uses functional decomposition, which does not handle changes in the requirements
- The very concept of microservice is misleading since there are only services. There is no sound definition of when a service starts or stops
being a microservice
- Data aggregation
Source:
Microservices - Wikipedia
9. Microservice
Benefits:
- Modularity
- Scalability
- Integration of heterogeneous and legacy systems
- Distributed development
Concerns:
- Services form information barriers
- Inter-service calls over a network have a higher cost
- Testing and deployment are more complicated
- Moving responsibilities between services is more difficult
- Viewing the size of services as the primary structuring mechanism can lead to too many services
- Two-phased commits are regarded as an anti-pattern
- Development and support of many services are more challenging if they are built with different tools and technologies
- The protocol typically used with microservices (HTTP) was designed for public-facing services, and as such is unsuitable for working
internal microservices that often must be impeccably reliable
- Decomposition methodology often uses functional decomposition, which does not handle changes in the requirements
- The very concept of microservice is misleading since there are only services. There is no sound definition of when a service starts or stops
being a microservice
- Data aggregation
Source:
Microservices - Wikipedia
10. Our case
- 3-9 people ✅
- Owns the product over its entire lifecycle 😐
- One team maintains a single microservice ❌
- Scalability ✅
- Integration of heterogeneous and legacy systems ✅
- Built with different tools and technologies 😐
- No sound definition of when a service starts or stops being a microservice
😐
- Dependencies between repositories ❌
Sources:
Own experience
15. Monorepo
Scalability
Scale service deployments independently
Fewer dependencies
Dramatically simplify the PR process
Less boilerplate
Dockerfile, ASDF .tool-versions, Ecto config, similar Mix deps everywhere, Github Actions workflows
Test wisely to save CI time
Always run integration tests, but only run the modified services’ unit tests