- External reference: https://microservices.io/patterns/data/transaction-log-tailing.html
- External reference: https://microservices.io/patterns/data/transactional-outbox.html
- External reference: https://microservices.io/patterns/data/aggregate.html
- External reference: https://microservices.io/patterns/data/domain-event.html
- External reference: https://microservices.io/patterns/data/event-sourcing.html
- External reference: https://microservices.io/patterns/data/cqrs.html
- External reference: https://microservices.io/patterns/server-side-discovery.html
- External reference: https://microservices.io/patterns/service-registry.html
- External reference: https://microservices.io/patterns/microservice-chassis.html
- External reference: https://microservices.io/patterns/client-side-discovery.html
- External reference: https://microservices.io/patterns/data/database-per-service.html
- External reference: https://microservices.io/patterns/microservices.html
Tail the database transaction log and publish each message/event inserted into the outbox to the message broker
Relatively obscure although becoming increasing common
Tricky to avoid duplicate publishing
How to reliably/atomically update the database and publish messages/events?
service that uses a relational database inserts messages/events into an outbox table
service that uses a NoSQL database appends the messages/events to attribute of the record
The service publishes high-level domain events
No 2PC required
The Event sourcing is an alternative solution
From Domain-Driven Design (DDD).
A graph of objects that can be treated as a unit
Organize the business logic of a service as a collection of DDD aggregates that emit domain events when they created or updated. The service publishes these domain events so that they can be consumed by other services
Event sourcing persists the state of a business entity such an Order or a Customer as a sequence of state-changing events. Whenever the state of a business entity changes, a new event is appended to the list of events. Since saving an event is a single operation, it is inherently atomic. The application reconstructs an entity’s current state by replaying the events
Applications persist events in an event store, which is a database of events.
The store has an API for adding and retrieving an entity’s events. The event store also behaves like a message broker. It provides an API that enables services to subscribe to events. When a service saves an event in the event store, it is delivered to all interested subscribers
order to optimize loading, an application can periodically save a snapshot of an entity’s current state. To reconstruct the current state, the application finds the most recent snapshot and the events that have occurred since that snapshot. As a result, there are fewer events to replay
solves one of the key problems in implementing an event-driven architecture and makes it possible to reliably publish events whenever state changes
Because it persists events rather than domain objects, it mostly avoids the object‑relational impedance mismatch problem
100% reliable audit log of the changes made to a business entity
different and unfamiliar style of programming and so there is a learning curve.
difficult to query since it requires typical queries to reconstruct the state of the business entities. That is likely to be complex and inefficient
As a result, the application must use Command Query Responsibility Segregation (CQRS) to implement queries. This in turn means that applications must handle eventually consistent data
Define a view database, which is a read-only replica that is designed to support that query. The application keeps the replica up to data by subscribing to Domain events published by the service that own the data.
Supports multiple denormalized views that are scalable and performant
Improved separation of concerns = simpler command and query models
- Potential code duplication
- Replication lag/eventually consistent views
When making a request to a service, the client makes a request via a router (a.k.a load balancer) that runs at a well known location. The router queries a service registry, which might be built into the router, and forwards the request to an available service instance.
Implement a service registry, which is a database of services, their instances and their locations. Service instances are registered with the service registry on startup and deregistered on shutdown. Client of the service and/or routers query the service registry to find the available instances of a service. A service registry might invoke a service instance’s health check API to verify that it is able to handle requests
Build your microservices using a microservice chassis framework, which handles cross-cutting concerns
The major benefit of a microservice chassis is that you can quickly and easy get started with developing a microservice
When making a request to a service, the client obtains the location of a service instance by querying a Service Registry, which knows the locations of all service instances
Keep each microservice’s persistent data private to that service and accessible only via its API. A service’s transactions only involve its database
service’s database is effectively part of the implementation of that service. It cannot be accessed directly by other services
Without some kind of barrier to enforce encapsulation, developers will always be tempted to bypass a service’s API and access it’s data directly.
Distributed transactions are best avoided because of the CAP theorem. Moreover, many modern (NoSQL) databases don’t support them.
There are various patterns/solutions for implementing transactions and queries that span services:
There are logical components corresponding to different functional areas of the application
team of developers
New team members must quickly become productive
each service is relatively small and so is easier to understand and change
inter-service communication mechanism and deal with partial failure
Implementing requests that span multiple services is more difficult
tangled dependencies might make it difficult to decompose your monolithic application into a set of services.
SRP defines a responsibility of a class as a reason to change, and states that a class should only have one reason to change. It make sense to apply the SRP to service design as well.
loose coupling, each service has its own database.
application must instead use the Saga pattern
service publishes an event when its data changes. Other services consume that event and update their data. There are several ways of reliably updating data and publishing events including Event Sourcing and Transaction Log Tailing
The Database per Service pattern describes how each service has its own database in order to ensure loose coupling
The API Gateway pattern defines how clients access the services in a microservice architecture