Edition 0 Updated to asp. Net core 0


public async Task< bool >  SaveEntitiesAsync (CancellationToken cancellationToken =  default



Yüklə 11,82 Mb.
Pdf görüntüsü
səhifə203/288
tarix12.07.2023
ölçüsü11,82 Mb.
#136458
1   ...   199   200   201   202   203   204   205   206   ...   288
public
async Task<
bool

SaveEntitiesAsync
(CancellationToken cancellationToken = 
default
(CancellationToken)) 

// Dispatch Domain Events collection.
// Choices:
// A) Right BEFORE committing data (EF SaveChanges) into the DB. This makes
// a single transaction including side effects from the domain event
// handlers that are using the same DbContext with Scope lifetime
// B) Right AFTER committing data (EF SaveChanges) into the DB. This makes
// multiple transactions. You will need to handle eventual consistency and
// compensatory actions in case of failures.
await _mediator.
DispatchDomainEventsAsync
(
this
); 
// After this line runs, all the changes (from the Command Handler and Domain
// event handlers) performed through the DbContext will be committed
var
result = await 
base
.
SaveChangesAsync
(); 


With this code, you dispatch the entity events to their respective event handlers. 
The overall result is that you’ve decoupled the raising of a 
domain event (a simple add into a list in 
memory) from dispatching it to an event handler. In addition, depending on what kind of dispatcher 
you are using, you could dispatch the events synchronously or asynchronously. 
Be aware that transactional boundaries come into significant play here. If your unit of work and 
transaction can span more than one aggregate (as when using EF Core and a relational database), this 
can work well. But if the transaction cannot span aggregates, you have to implement additional steps 
to achieve consistency. This is another reason why persistence ignorance is not universal; it depends 
on the storage system you use. 
Single transaction across aggregates versus eventual consistency across 
aggregates 
The question of whether to perform a single transaction across aggregates versus relying on eventual 
consistency across those aggregates is a controversial one. Many DDD authors like Eric Evans and 
Vaughn Vernon advocate the rule that one transaction = one aggregate and therefore argue for 
eventual consistency across aggregates. For example, in his book 
Domain-Driven Design
, Eric Evans 
says this: 
Any rule that spans Aggregates will not be expected to be up-to-date at all times. Through event 
processing, batch processing, or other update mechanisms, other dependencies can be resolved 
within some specific time. (page 128) 


235 
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns 
Vaughn Vernon says the following in 
Effective Aggregate Design. Part II: Making Aggregates Work 
Together

Thus, if executing a command on one aggregate instance requires that additional business rules 
execute on one or more aggregates, use eventual consistency […] There is a practical w
ay to support 
eventual consistency in a DDD model. An aggregate method publishes a domain event that is in time 
delivered to one or more asynchronous subscribers. 
This rationale is based on embracing fine-grained transactions instead of transactions spanning many 
aggregates or entities. The idea is that in the second case, the number of database locks will be 
substantial in large-scale applications with high scalability needs. Embracing the fact that highly 
scalable applications need not have instant transactional consistency between multiple aggregates 
helps with accepting the concept of eventual consistency. Atomic changes are often not needed by 
the business, and it is in any case the responsibility of the domain experts to say whether particular 
operations need atomic transactions or not. If an operation always needs an atomic transaction 
between multiple aggregates, you might ask whether your aggregate should be larger or wasn’t 
correctly designed. 
However, other developers and architects like Jimmy Bogard are okay with spanning a single 
transaction across several aggregates

but only when those additional aggregates are related to side 
effects for the same original command. For instance, in 
A better domain events pattern
, Bogard says 
this: 
Typically, I want the side effects of a domain event to occur within the same logical transaction, but 
not necessarily in the same scope of r
aising the domain event […] Just before we commit our 
transaction, we dispatch our events to their respective handlers. 
If you dispatch the domain events right 
before
committing the original transaction, it is because you 
want the side effects of those events to be included in the same transaction. For example, if the EF 
DbContext SaveChanges method fails, the transaction will roll back all changes, including the result of 
any side effect operations implemented by the related domain event handlers. This is because the 
DbContext life scope is by default defined as “scoped.” Therefore, the DbContext object is shared 
across multiple repository objects being instantiated within the same scope or object graph. This 
coincides with the HttpRequest scope when developing Web API or MVC apps. 
Actually, both approaches (single atomic transaction and eventual consistency) can be right. It really 
depends on your domain or business requirements and what the domain experts tell you. It also 
depends on how scalable you need the service to be (more granular transactions have less impact 
with regard to database locks). And it depends on how much investment you’re willing to make in 
your code, since eventual consistency requires more complex code in order to detect possible 
inconsistencies across aggregates and the need to implement compensatory actions. Consider that if 
you commit changes to the original aggregate and afterwards, when the events are being dispatched, 
if there’s an issue and the event handlers cannot commit their
side effects, you’ll have inconsistencies 
between aggregates. 
A way to allow compensatory actions would be to store the domain events in additional database 
tables so they can be part of the original transaction. Afterwards, you could have a batch process that 
detects inconsistencies and runs compensatory actions by comparing the list of events with the 


236 
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns 
current state of the aggregates. The compensatory actions are part of a complex topic that will require 
deep analysis from your side, which includes discussing it with the business user and domain experts. 
In any case, you can choose the approach you need. But the initial deferred approach

raising the 
events before committing, so you use a single transaction

is the simplest approach when using EF 
Core and a 
relational database. It’s easier to implement and valid in many business cases. It’s also the 
approach used in the ordering microservice in eShopOnContainers. 
But how do you actually dispatch those events to their respective event handlers? What’s the 
_mediator
object you see in the previous example? It has to do with the techniques and artifacts you 
use to map between events and their event handlers. 

Yüklə 11,82 Mb.

Dostları ilə paylaş:
1   ...   199   200   201   202   203   204   205   206   ...   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