Edition 0 Updated to asp. Net core 0



Yüklə 11,82 Mb.
Pdf görüntüsü
səhifə249/288
tarix12.07.2023
ölçüsü11,82 Mb.
#136458
1   ...   245   246   247   248   249   250   251   252   ...   288
public
class
LoggingBehavior 
: IPipelineBehavior 

private
readonly
ILogger> _logger; 
public
LoggingBehavior
(ILogger> logger) => 


289 
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns 
_logger = logger; 
public
async Task 
Handle
(TRequest request, 
RequestHandlerDelegate next) 

_logger.
LogInformation
($
"Handling {typeof(TRequest).Name}"
); 
var
response = await 
next
(); 
_logger.
LogInformation
($
"Handled {typeof(TResponse).Name}"
); 
return
response; 


Just by implementing this behavior class and by registering it in the pipeline (in the MediatorModule 
above), all the commands processed through MediatR will be logging information about the 
execution. 
The eShopOnContainers ordering microservice also applies a second behavior for basic validations, 
the 
ValidatorBehavior
 class that relies on the 
FluentValidation
 library, as shown in the following code: 
public
class
ValidatorBehavior 
: IPipelineBehavior 

private
readonly
IValidator[] _validators; 
public
ValidatorBehavior
(IValidator[] validators) => 
_validators = validators; 
public
async Task 
Handle
(TRequest request, 
RequestHandlerDelegate next) 

var
failures = _validators 
.
Select
(v => v.
Validate
(request)) 
.
SelectMany
(result => result.
Errors

.
Where
(error => error != 
null

.
ToList
(); 
if
(failures.
Any
()) 

throw
new
OrderingDomainException

$
"Command Validation Errors for type {typeof(TRequest).Name}"

new
ValidationException
(
"Validation exception"
, failures)); 

var
response = await 
next
(); 
return
response; 


Here the behavior is raising an exception if validation fails, but you could also return a result object, 
containing the command result if it succeeded or the validation messages in case it didn’t. This would 
probably make it easier to display validation results to the user. 
Then, based on the 
FluentValidation
 library, you would create validation for the data passed with 
CreateOrderCommand, as in the following code: 
public
class
CreateOrderCommandValidator : AbstractValidator 

public
CreateOrderCommandValidator
() 



290 
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns 
RuleFor
(command => command.
City
).
NotEmpty
(); 
RuleFor
(command => command.
Street
).
NotEmpty
(); 
RuleFor
(command => command.
State
).
NotEmpty
(); 
RuleFor
(command => command.
Country
).
NotEmpty
(); 
RuleFor
(command => command.
ZipCode
).
NotEmpty
(); 
RuleFor
(command => command.
CardNumber
).
NotEmpty
().
Length
(
12

19
); 
RuleFor
(command => command.
CardHolderName
).
NotEmpty
(); 
RuleFor
(command => 
command.
CardExpiration
).
NotEmpty
().
Must
(BeValidExpirationDate).
WithMessage
(
"Please specify 
a valid card expiration date"
); 
RuleFor
(command => command.
CardSecurityNumber
).
NotEmpty
().
Length
(
3
); 
RuleFor
(command => command.
CardTypeId
).
NotEmpty
(); 
RuleFor
(command => command.
OrderItems
).
Must
(ContainOrderItems).
WithMessage
(
"No 
order items found"
); 

private
bool
BeValidExpirationDate
(DateTime dateTime) 

return
dateTime >= DateTime.
UtcNow


private
bool
ContainOrderItems
(IEnumerable orderItems) 

return
orderItems.
Any
(); 


You could create additional validations. This is a very clean and elegant way to implement your 
command validations. 
In a similar way, you could implement other behaviors for additional aspects or cross-cutting concerns 
that you want to apply to commands when handling them. 
Additional resources 
The mediator pattern 

Mediator pattern
https://en.wikipedia.org/wiki/Mediator_pattern
 
The decorator pattern 

Decorator pattern
https://en.wikipedia.org/wiki/Decorator_pattern
 
MediatR (Jimmy Bogard) 

MediatR.
GitHub repo. 
https://github.com/jbogard/MediatR
 

CQRS with MediatR and AutoMapper
https://lostechies.com/jimmybogard/2015/05/05/cqrs-with-mediatr-and-automapper/
 

Put your controllers on a diet: POSTs and commands.
https://lostechies.com/jimmybogard/2013/12/19/put-your-controllers-on-a-diet-posts-and-
commands/
 


291 
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns 

Tackling cross-cutting concerns with a mediator pipeline
https://lostechies.com/jimmybogard/2014/09/09/tackling-cross-cutting-concerns-with-a-
mediator-pipeline/
 

CQRS and REST: the perfect match
https://lostechies.com/jimmybogard/2016/06/01/cqrs-and-rest-the-perfect-match/
 

MediatR Pipeline Examples
https://lostechies.com/jimmybogard/2016/10/13/mediatr-pipeline-examples/
 

Vertical Slice Test Fixtures for MediatR and ASP.NET Core
https://lostechies.com/jimmybogard/2016/10/24/vertical-slice-test-fixtures-for-mediatr-and-
asp-net-core/
 

MediatR Extensions for Microsoft Dependency Injection Released
https://lostechies.com/jimmybogard/2016/07/19/mediatr-extensions-for-microsoft-
dependency-injection-released/
 
Fluent validation 

Jeremy Skinner. FluentValidation.
GitHub repo. 
https://github.com/JeremySkinner/FluentValidation
 


292 
CHAPTER 7 | Implement resilient applications 
CHAPTER 

Implement resilient 
applications 
Your microservice and cloud-based applications must embrace the partial failures that will certainly 
occur eventually. You must design your application to be resilient to those partial failures.
Resiliency is the ability to recover from failures and continue to function. It isn’t about avoiding 
failures but accepting the fact that failures will happen and responding to them in a way that avoids 
downtime or data loss. The goal of resiliency is to return the application to a fully functioning state 
after a failure. 
It’s challenging enough to design and deploy a microservices
-based application. But you also need to 
keep your application running in an environment where some sort of failure is certain. Therefore, your 
application should be resilient. It should be designed to cope with partial failures, like network 
outages or nodes or VMs crashing in the cloud. Even microservices (containers) being moved to a 
different node within a cluster can cause intermittent short failures within the application. 
The many individual components of your application should also incorporate health monitoring 
features. By following the guidelines in this chapter, you can create an application that can work 
smoothly in spite of transient downtime or the normal hiccups that occur in complex and cloud-based 
deployments. 
Important 
eShopOnContainer had been using the 
Polly library
 to implement resiliency using 
Typed Clients
 up 
until the release 3.0.0. 
Starting with release 3.0.0, the HTTP calls resiliency is implemented using a 
Linkerd mesh
, that handles 
retries in a transparent and configurable fashion, within a Kubernetes cluster, without having to 
handle those concerns in the code. 
The Polly library is still used to add resilience to database connections, specially while starting up the 
services. 
Warning 
All code samples and images in this section were valid before using Linkerd and are not updated to 
reflect the current actual code. So they make sense in the context of this section. 


293 
CHAPTER 7 | Implement resilient applications 
Handle partial failure 
In distributed systems like microservices-
based applications, there’s an ever
-present risk of partial 
failure. For instance, a single microservice/container can fail or might not be available to respond for a 
short time, or a single VM or server can crash. Since clients and services are separate processes, a 
service might not be able to respond in a timely way to a client’s request. The service might be 
overloaded and responding very slowly to requests or might simply not be accessible for a short time 
because of network issues. 
For example, consider the Order details page from the eShopOnContainers sample application. If the 
ordering microservice is unresponsive when the user tries to submit an order, a bad implementation 
of the client process (the MVC web application)

for example, if the client code were to use 
synchronous RPCs with no timeout

would block threads indefinitely waiting for a response. Besides 
creating a bad user experience, every unresponsive wait consumes or blocks a thread, and threads are 
extremely valuable in highly scalable applications. If there are many blocked threads, eventually the 
application’s runtime can run out of threads. In that case, the application can become globally 
unresponsive instead of just partially unresponsive, as shown in Figure 8-1. 
Figure 8-1. Partial failures because of dependencies that impact service thread availability 
In a large microservices-based application, any partial failure can be amplified, especially if most of 
the internal microservices interaction is based on synchronous HTTP calls (which is considered an anti-
pattern). Think about a system that receives millions of incoming calls per day. If your system has a 
bad design that’s based on long chains of synch
ronous HTTP calls, these incoming calls might result in 
many more millions of outgoing calls (let’s suppose a ratio of 1:4) to dozens of internal microservices 
as synchronous dependencies. This situation is shown in Figure 8-2, especially dependency #3, that 
starts a chain, calling dependency #4, which then calls #5. 


294 
CHAPTER 7 | Implement resilient applications 
Figure 8-2. The impact of having an incorrect design featuring long chains of HTTP requests 
Intermittent failure is guaranteed in a distributed and cloud-based system, even if every dependency 
itself has excellent availability. It’s a fact you need to consider.
If you do not design and implement techniques to ensure fault tolerance, even small downtimes can 
be amplified. As an example, 50 dependencies each with 99.99% of availability would result in several 
hours of downtime each month because of this ripple effect. When a microservice dependency fails 
while handling a high volume of requests, that failure can quickly saturate all available request threads 
in each service and crash the whole application. 
Figure 8-3. Partial failure amplified by microservices with long chains of synchronous HTTP calls 


295 
CHAPTER 7 | Implement resilient applications 
To minimize this problem, in the section 
Asynchronous microservice integrat
ion enforce microservice’s 
autonomy
, this guide encourages you to use asynchronous communication across the internal 
microservices. 
In addition, it’s essential that you design your microservices and client 
applications to handle partial 
failures

that is, to build resilient microservices and client applications. 
Strategies to handle partial failure 
To deal with partial failures, use one of the strategies described here. 

Yüklə 11,82 Mb.

Dostları ilə paylaş:
1   ...   245   246   247   248   249   250   251   252   ...   288




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©azkurs.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin