amantinband / clean-architecture

The ultimate clean architecture template for .NET applications 💪
MIT License
1.4k stars 221 forks source link

Query on Eventual Consistency Mechanism #16

Closed tiagofvital closed 5 months ago

tiagofvital commented 6 months ago

Hi @amantinband!

Firstly, thank you for this amazing project! It's packed with fantastic features. 🙌

However, I have a question regarding how eventual consistency is ensured. If I understood correctly, domain events are stored in the HttpContext Items and then processed after the response is sent to the client. In the event of a server breakdown, wouldn't there be a risk of inconsistency?

Wouldn't it be more advisable to implement an outbox pattern for such scenarios?

Thank you!

amantinband commented 6 months ago

Hey @tiagofvital! The code snippets in the README.md are a simplified. Here is the full implementation:

image

As you can see above, all the changes made in the scope of a single request are wrapped inside a transaction.

This transaction is only committed after all side effects took place. Meaning, that in the worse case scenario, If error handing wasn't designed resiliently, the user will think everything happend successfully since they will get a 2XX response, but the changes won't be stored in the database.

tiagofvital commented 6 months ago

Got it! 👍 In that case, I'm a bit uncertain about the advantages of this approach compared to dispatching domain events before calling dbContext SaveChanges. Using the Create Subscription feature as an example, wouldn't it be acceptable to have something like:

var subscription = new Subscription(request.SubscriptionType);

var user = new User(
    request.UserId,
    request.FirstName,
    request.LastName,
    request.Email,
    subscription);

await _usersRepository.AddAsync(user, cancellationToken);

await _usersRepository.UnitOfWork.SaveChanges();

return SubscriptionResult.FromUser(user);

The unit of work would then dispatch the domain events after committing changes in the dbContext.

This would be an alternative way to handle it. What are your thoughts?"

IsItBroken commented 6 months ago

Got it! 👍 In that case, I'm a bit uncertain about the advantages of this approach compared to dispatching domain events before calling dbContext SaveChanges. Using the Create Subscription feature as an example, wouldn't it be acceptable to have something like:

var subscription = new Subscription(request.SubscriptionType);

var user = new User(
    request.UserId,
    request.FirstName,
    request.LastName,
    request.Email,
    subscription);

await _usersRepository.AddAsync(user, cancellationToken);

await _usersRepository.UnitOfWork.SaveChanges();

return SubscriptionResult.FromUser(user);

The unit of work would then dispatch the domain events after committing changes in the dbContext.

This would be an alternative way to handle it. What are your thoughts?"

In his course, he discusses the advantages of dispatching domain events after the main operation. This approach primarily enhances user experience by providing a perceived performance boost. When domain events are dispatched post-operation, the user isn't kept waiting for all the side effects and event handling to complete. This is particularly beneficial in a microservices architecture where some side effects might depend on other services.

If these services are down, the system can still return a success response to the user, while a retry logic works in the background to handle the events once dependencies are back online. This not only improves the system's responsiveness but also its fault tolerance. However, it's important to consider the additional complexity this might introduce, especially in terms of failure handling and maintaining data consistency.

amantinband commented 6 months ago

Right, @IsItBroken gave the full answer ❤️

The difference is this line: https://github.com/amantinband/clean-architecture/blob/09605637026f5e05375969b4fedb6c797dabe736/src/CleanArchitecture.Infrastructure/Common/Middleware/EventualConsistencyMiddleware.cs#L16

The entire process happens only after the user gets the response

amantinband commented 5 months ago

Closing this issue. Feel free to reopen if you have further questions

Luiz-Franco commented 1 month ago

Hi @amantinband!

What happens when there is a crash before committing the transaction? Wouldn't the client application wrongly receive a success status?