Scalability model (3 ways to scale an application)
X-axis: scaling the app from monolith to many instances (scaling by cloning - horizontal duplication)
Y-axis: scaling in the sense of the functional decomposition
into microservices
Z-axis: scaling across “data partitioned” instances
splitting data by some sharding key (could be customer ID)
Why is scaling microservices better than scaling a monolith?
monolith has all functionalities in one process and has to be scaled (= copied to multiple servers) as a whole
microservices are loosely composed of many independent functionalities and each one of them could be scaled differently (as needed) on multiple servers
Characteristics of microservices
loosely coupled (well defined interfaces)
using HTTP with REST principles (everybody can understand)
could be deployed independently
are easily replaced, only part of the system is being replaced
owned by a small team
their organization reflect the business (accounting, inventory etc.)
each microservice could be written in different programming language (better for the problem that’s being solved by the service)
Overview of the microservice patterns
Database per service
each service have it’s own database or schema
pros:
truly independent
database could be designed tailored to the microservice needs (different technologies could be used)
cons:
complex queries
difficult data consistency mechanisms
Data consistency mechanisms (2 alternatives)
Saga pattern
each service have it’s own “local transactions” and they produce events (and listen to events from other services)
if the service receives a message from another, it can act on it and produce another message with next steps/result etc.
choreography-based sagas
no central coordinator, each service listens to all services
orchestration-based sagas
master coordinates the local transactions
Two-phase commit
for each process, the Transaction manager has two phases:
commit-request phase
each service (resource manager) involved is asked to perform the operation and send the agreement (whether the action was successful or not)
commit phase
the results from the previous phase are committed / rollbacked (depending on the answer of the resource manager)
CQRS = Command Query Responsibility Segregation
separates the write and read operations
the read operations are optimized and faster
the get the message from write component involving the information about the written data
API gateway
single access point for clients, which then redirects the requests to the correct services
pros:
clients need to know only one address
acts as cache, implements authentication, request aggregation, protocol translation etc.
cons:
single point of failure, potential bottleneck
could be of complex implementation
Aggregator design pattern
combines data from multiple services into a single response
pros:
reduces the number of requests made by a client
simplifies the client logic
cons:
often complex implementation
could be a bottleneck
Circuit breaker
a service, which monitors other services for fails
if some fail rate at some service exceeds given threshold, the Circuit breaker “opens” the circuit with blocking call to that service (giving it time to recover)
after given time, it allows to “test” the service with limited number of calls
if the service is responding fine, Circuit breaker “closes” the circuit and allows normal traffic
pros:
prevents the cascading failures in distributed systems
improves system resilience and stability
cons:
the configuration (thresholds, timeouts) has to be precise and careful
Sidecar pattern
defines helper services to main services (for logging, monitoring, configuration etc.)
are deployed on separate processes or containers (but they share the lifecycle)
pros:
could help more main services at the same time
decouples the “helper” methods from the main service
cons:
the system overall is more complex
more containers and more processes needed
Strangler pattern
for turning a monolith to microservices
new features are implemented as microservices and are being connected to the remaining monolith
existing functionality is slowly “strangled” and turned into microservices
pros:
helping with transition to microservices (system is not rewritten at once)
gradual migrations and testing more possible
cons:
performance overhead during transition
both systems have to be managed at the same time (complex on infrastructure)