Microservice Patterns Flashcards
What kind of 5 categories of Microservice patterns do you know?
1) Decomposition pattern
2) Integration Patterns
3) Database Patterns
4) Observability patterns
5) Cross-Cutting Concern Patterns
What are the steps for defining an application’s microservice architecture?
[1] IDENTIFY SYSTEM OPERATIONS
Describe Functional requirements:
“As a consumer I want to place an order so that I can …”
[2] IDENTIFY SERVICES
There are different strategies.
a) define services corresponding to business capabilities
b) Organize services around domain-driven-design
eg:
OrderService, KitchenService, RestaurantService …
[3] Define Service APIs and collaborations
a) OrderService “talks” with RestaurantService to check if ordered product exist
OrderService ——> (verifyOrder) —-> RestaurantService
What are the obstacles to decomposition?
[1] NETWORK LATENCY
You might discover that a particular decomposition would be impractical due to too many round-trips between services
[2] REDUCED AVAILABILITY
synchronous communication between services reduces availability
[3] DATA CONSITENCY
The third obstacle is the requirement to maintain data consistency across services. You’ll typically need to use sagas
[4] Obtaining consistent view of the data
[5] God classes preventing decomposition
What is the difference between Decompose by business Capability and by Subdomain?
[1] Decompose by Business Capability
Problem: Microservices is all about making services loosely coupled and applying the single responsibility principle. However, breaking an application into smaller pieces has to be done logically. How do we decompose an application into small services?
Solution: One strategy is to decompose by business capability. A business capability is something that a business does in order to generate value. The set of capabilities for a given business depend on the type of business. For example, the capabilities of an insurance company typically include sales, marketing, underwriting, claims processing, billing, compliance, etc. Each business capability can be thought of as a service, except it’s business-oriented rather than technical.
[2] Decompose by Subdomain
Problem: Decomposing an application using business capabilities might be a good start, but you will come across so-called “God Classes” which will not be easy to decompose. These classes will be common among multiple services. For example, the Order class will be used in Order Management, Order Taking, Order Delivery, etc. How do we decompose them?
Solution: For the “God Classes” issue, DDD (Domain-Driven Design) comes to the rescue. It uses subdomains and bounded context concepts to solve this problem. DDD breaks the whole domain model created for the enterprise into subdomains. Each subdomain will have a model, and the scope of that model will be called the bounded context. Each microservice will be developed around the bounded context.
What is “Common Closure Principle” and how does it help to design microservices?
The idea is that if two classes change in lockstep because of the same underlying reason, then they belong in the same package. Perhaps, for example, those classes implement a different aspect of a particular business rule. The goal is that when that business rule changes, developers only need to change code in a small number of packages (ideally only one). Adhering to the CCP significantly improves the maintainability of an application.
We can apply CCP when creating a microservice architecture and package components that change for the same reason into the same service. Doing this will minimize the number of services that need to be changed and deployed when some requirement changes. Ideally, a change will only affect a single team and a single service. CCP is the antidote to the distributed monolith anti-pattern.
What is the Network latency problem and how to solve it?
Network latency is an ever-present concern in a distributed system. You might discover that a particular decomposition into services results in a large number of round-trips between two services. Sometimes, you can reduce the latency to an acceptable amount by implementing a batch API for fetching multiple objects in a single round trip. But in other situations, the solution is to combine services, replacing expensive IPC with language-level method or function calls.
How to solve the problem of reduced availability of microservices when using synchronous communication?
Instead of using synchronous communication like REST use asynchronous communication.
How to solve the problem of data consistency across services?
The solution is to use the SAGA pattern.
Another challenge is maintaining data consistency across services. Some system operations need to update data in multiple services. For example, when a restaurant accepts an order, updates must occur in both the Kitchen Service and the Delivery Service. The Kitchen Service changes the status of the Ticket. The Delivery Service schedules delivery of the order. Both of these updates must be done atomically.
The traditional solution is to use a two-phase, commit-based, distributed transaction management mechanism. But as you’ll see in chapter 4, this is not a good choice for modern applications, and you must use a very different approach to transaction management, a saga. A saga is a sequence of local transactions that are coordinated using messaging. Sagas are more complex than traditional ACID transactions but they work well in many situations. One limitation of sagas is that they are eventually consistent. If you need to update some data atomically, then it must reside within a single service, which can be an obstacle to decomposition.
How to solve the problem of “not obtaining a consistent view of the data”?
Another obstacle to decomposition is the inability to obtain a truly consistent view of data across multiple databases. In a monolithic application, the properties of ACID transactions guarantee that a query will return a consistent view of the database. In contrast, in a microservice architecture, even though each service’s database is consistent, you can’t obtain a globally consistent view of the data. If you need a consistent view of some data, then it must reside in a single service, which can prevent decomposition. Fortunately, in practice, this is rarely a problem.
What are god classes and how God classes prevent decomposition?
A god class typically implements business logic for many different aspects of the application. It normally has a large number of fields mapped to a database table with many columns. Most applications have at least one of these classes, each representing a concept that’s central to the domain: accounts in banking, orders in e-commerce, policies in insurance, and so on. Because a god class bundles together state and behavior for many different aspects of an application, it’s an insurmountable obstacle to splitting any business logic that uses it into services.
[1] NOT OPTIMAL SOLUTION
One solution is to package the Order class into a library and create a central Order database. All services that process orders use this library and access the access database. The trouble with this approach is that it violates one of the key principles of the microservice architecture and results in undesirable, tight coupling. For example, any change to the Order schema requires the teams to update their code in lockstep.
[2] BETTER SOLUTION
A much better approach is to apply DDD and treat each service as a separate subdomain with its own domain model.
This means that each of the services in the FTGO application that has anything to do with orders has its own domain model with its version of the Order class.
A great example of the benefit of multiple domain models is the Delivery Service. Its view of an Order, shown in figure 2.11, is extremely simple: pickup address, pickup time, delivery address, and delivery time. Moreover, rather than call it an Order, the Delivery Service uses the more appropriate name of Delivery.
The Kitchen Service also has a much simpler view of an order. Its version of an Order is called a Ticket. As figure 2.12 shows, a Ticket simply consist of a status, the requestedDeliveryTime, a prepareByTime, and a list of line items that tell the restaurant what to prepare. It’s unconcerned with the consumer, payment, delivery, and so on.
What are client-service integration styles?
[1] Asynchronous and synchronous
[2] One-to-one (queuing); One-to-Many (Pub/Sub)
What are the two types of messages? Give examples.
1) text (JSON, XML, YAML)
2) binary (AVRO, Protocol Buffers)
What are the pros and cons of text-type messages?
PROS:
1) human readable
2) self describing
CONS:
1) the messages tend to be verbose, especially XML. Every message has the overhead of containing the names of the attributes in addition to their values.
2) the overhead of parsing text, especially when messages are large. Consequently, if efficiency and performance are important, you may want to consider using a binary format.
What are the pros and cons of binary-type messages?
PROS:
a) smaller size than text-type messages
CONS:
a) format is binary so we can’t see the message if want to view what was actually sent
What is the difference between Protocol Bufferes message type and AVRO?
One difference between these two binary formats is that Protocol Buffers uses tagged fields, whereas an Avro consumer needs to know the schema in order to interpret messages. As a result, handling API evolution is easier with Protocol Buffers than with Avro