Enterprise Architecture

Overview

Proxy Model
The Proxy Model is a relatively simple networking model. It’s an excellent starting point for an initial microservices application, or as a target model in converting a moderately complex monolithic legacy app.
In the Proxy Model, we can use something like NGINX or NGINX Plus that acts as an ingress controller, routing requests to microservices as an API Gateway.
The function of the API Gateway can allow for protocol changes (i.e incoming as http outgoing as mttq), allows for rate limiting or traffic throttling, blue/green deployments and canary deployments.
This is similar in pattern style to the Java Facade.
The benefits for this model is that there is one url endpoint, with variations in the url pattern that the "api-gateway" will then route accordingly (reverse-proxy) to the relevant microsrevice. Its fairly simple to implement and deploy.
With regards to Openshift this pattern is used between services and pods. However the service (which in fact is an HAProxy service) will round robin requests to each pod replica's that have been created and registered.

Aggregator Pattern
The Aggregator pattern helps to address this. It talks about how we can aggregate the data from different services and then send the final response to the consumer. This can be done in two ways:
1. A composite microservice will make calls to all the required microservices, consolidate the data, and transform the data before sending back.
2. An API Gateway can also partition the request to multiple microservices and aggregate the data before sending it to the consumer.
It is recommended if any business logic is to be applied, then choose a composite microservice. Otherwise, the API Gateway is the established solution.

Decomposition Pattern
Ref : Rajesh Bhojwani Solution Architect, DELLEMC

 1. Decompose by business capabilities
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
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.

Note: Identifying subdomains is not an easy task. It requires an understanding of the business. Like business capabilities, subdomains are identified by analyzing the business and its organizational structure and identifying the different areas of expertise.

Strangler Pattern
The Strangler pattern comes to the rescue. The Strangler pattern is based on an analogy to a vine that strangles a tree that it’s wrapped around. This solution works well with web applications, where a call goes back and forth, and for each URI call, a service can be broken into different domains and hosted as separate services. The idea is to do it one domain at a time. This creates two separate applications that live side by side in the same URI space. Eventually, the newly refactored application “strangles” or replaces the original application until finally you can shut off the monolithic application

Client Side Decomposition
Also know as the scatter and gather pattern.
With microservices, the UI has to be designed as a skeleton with multiple sections/regions of the screen/page. Each section will make a call to an individual backend microservice to pull the data. That is called composing UI components specific to service. Frameworks like AngularJS and ReactJS help to do that easily. These screens are known as Single Page Applications (SPA). This enables the app to refresh a particular region of the screen instead of the whole page.

Data Pattern
Database per service
There is a problem of how to define database architecture for microservices. Following are the concerns to be addressed:
 1. Services must be loosely coupled. They can be developed, deployed, and scaled independently.
 2. Business transactions may enforce invariants that span multiple services.
 3. Some business transactions need to query data that is owned by multiple services.
 4. Databases must sometimes be replicated and sharded in order to scale.
 5. Different services have different data storage requirements.

Solution
To solve the above concerns, one database per microservice must be designed; it must be private to that service only. It should be accessed by the microservice API only. It cannot be accessed by other services directly. For example, for relational databases, we can use private-tables-per-service, schema-per-service, or database-server-per-service. Each microservice should have a separate database id so that separate access can be given to put up a barrier and prevent it from using other service tables.

Shared database per service
If the application is a monolith and trying to break into microservices, denormalization is not that easy. A shared database per service is not ideal, but that is the working solution for the stated scenario. Most people consider this an anti-pattern for microservices, but for brownfield applications, this is a good start to break the application into smaller logical pieces. This should not be applied for greenfield applications. In this pattern, one database can be aligned with more than one microservice, but it has to be restricted to 2-3 maximum, otherwise scaling, autonomy, and independence will be challenging to execute.

Command Query Responsibility Segregation (CQRS)
Problem
Once we implement database-per-service, there is a requirement to query, which requires joint data from multiple services — it's not possible. Then, how do we implement queries in microservice architecture?
Solution
CQRS suggests splitting the application into two parts — the command side and the query side. The command side handles the Create, Update, and Delete requests. The query side handles the query part by using the materialized views. The event sourcing pattern is generally used along with it to create events for any data change. Materialized views are kept updated by subscribing to the stream of events.

Saga Pattern
Problem
When each service has its own database and a business transaction spans multiple services, how do we ensure data consistency across services? For example, for an e-commerce application where customers have a credit limit, the application must ensure that a new order will not exceed the customer’s credit limit. Since Orders and Customers are in different databases, the application cannot simply use a local ACID transaction.
Solution
A Saga represents a high-level business process that consists of several sub requests, which each update data within a single service. Each request has a compensating request that is executed when the request fails. It can be implemented in two ways:
Choreography — When there is no central coordination, each service produces and listens to another service’s events and decides if an action should be taken or not.
Orchestration — An orchestrator (object) takes responsibility for a saga’s decision making and sequencing business logic.

Cross Cutting Concern
External Configuration
Problem
A service typically calls other services and databases as well. For each environment like dev, QA, UAT, prod, the endpoint URL or some configuration properties might be different. A change in any of those properties might require a re-build and re-deploy of the service. How do we avoid code modification for configuration changes?
Solution
Externalize all the configuration, including endpoint URLs and credentials. The application should load them either at startup or on the fly.
Spring Cloud config server provides the option to externalize the properties to GitHub and load them as environment properties. These can be accessed by the application on startup or can be refreshed without a server restart.

Service Discovery Pattern
Problem
When microservices come into the picture, we need to address a few issues in terms of calling services:
With container technology, IP addresses are dynamically allocated to the service instances. Every time the address changes, a consumer service can break and need manual changes.
Each service URL has to be remembered by the consumer and become tightly coupled.
So how does the consumer or router know all the available service instances and locations?
Solution
A service registry needs to be created which will keep the metadata of each producer service. A service instance should register to the registry when starting and should de-register when shutting down. The consumer or router should query the registry and find out the location of the service. The registry also needs to do a health check of the producer service to ensure that only working instances of the services are available to be consumed through it. There are two types of service discovery: client-side and server-side. An example of client-side discovery is Netflix Eureka and an example of server-side discovery is AWS ALB.

Circuit Breaker Pattern
Problem
A service generally calls other services to retrieve data, and there is the chance that the downstream service may be down. There are two problems with this: first, the request will keep going to the down service, exhausting network resources and slowing performance. Second, the user experience will be bad and unpredictable. How do we avoid cascading service failures and handle failures gracefully?
Solution
The consumer should invoke a remote service via a proxy that behaves in a similar fashion to an electrical circuit breaker. When the number of consecutive failures crosses a threshold, the circuit breaker trips, and for the duration of a timeout period, all attempts to invoke the remote service will fail immediately. After the timeout expires the circuit breaker allows a limited number of test requests to pass through. If those requests succeed, the circuit breaker resumes normal operation. Otherwise, if there is a failure, the timeout period begins again.
Netflix Hystrix is a good implementation of the circuit breaker pattern. It also helps you to define a fallback mechanism which can be used when the circuit breaker trips. That provides a better user experience.

Blue-Green Deployment Pattern
Problem
With microservice architecture, one application can have many microservices. If we stop all the services then deploy an enhanced version, the downtime will be huge and can impact the business. Also, the rollback will be a nightmare. How do we avoid or reduce downtime of the services during deployment?
Solution
The blue-green deployment strategy can be implemented to reduce or remove downtime. It achieves this by running two identical production environments, Blue and Green. Let's assume Green is the existing live instance and Blue is the new version of the application. At any time, only one of the environments is live, with the live environment serving all production traffic. All cloud platforms provide options for implementing a blue-green deployment.
There are many other patterns used with microservice architecture, like Sidecar, Chained Microservice, Branch Microservice, Event Sourcing Pattern, Continuous Delivery Patterns, and more.

Observability Patterns
Log Aggregation
Problem
Consider a use case where an application consists of multiple service instances that are running on multiple machines. Requests often span multiple service instances. Each service instance generates a log file in a standardized format. How can we understand the application behavior through logs for a particular request?
Solution
We need a centralized logging service that aggregates logs from each service instance. In Openshift we can make use of ELK (elastic search, logstash and Kibana)

Performance Metrics
Problem
When the service portfolio increases due to microservice architecture, it becomes critical to keep a watch on the transactions so that patterns can be monitored and alerts sent when an issue happens. How should we collect metrics to monitor application perfomance?
Solution
Again within Openshift we can make use of prometheus, also hawkular, cassandra and heapster to monitor all pods (cpu, memory, io usage and networking)

Health Check
Problem
When microservice architecture has been implemented, there is a chance that a service might be up but not able to handle transactions. In that case, how do you ensure a request doesn't go to those failed instances? With a load balancing pattern implementation.
Solution
Openshift/Kubernetes has the ability to use liveliness and readiness probes, configured in the DeployentConfig file. The readiness probe tells the pod that it is ready to receive traffic, while the liveliness probe is used to ensure the pod is always available.
About
This is a collection of data for a quick lookup / reference
 
Its not an exhaustive reference (nor will it be), but as stated before its a quick lookup / reference
LMZ 2020