Giter Site home page Giter Site logo

Comments (1)

mauroservienti avatar mauroservienti commented on May 18, 2024

Hi again 😄 and thanks for having waited for me.

Can you pls show how to compensate in case on of the handlers fail to process the request? What would be the valid reason to fail? business exception? The request contains invalid data? Or should we strive to make sure our command is valid in 99% and dont perform any compensation requests?

In this specific sample, there isn't really any scenario that comes to my mind that can lead to a failed request. That's the main reason that leads me to entirely remove the whole CleanupFailedCartRequest thing in #101 and to update the talk to mention the new way. The latest version of the slides (and a recording) is available here: https://milestone.topics.it/events/all-our-aggregates-are-wrong-webinar.html

In summary, the previous approach on HTTP requests, and when in need of adding a new item to the cart the gateway had to orchestrate across 3 different outgoing POST requests, each one to a different service. If something goes badly in such a scenario there is no way for the gateway to make sure all requests are rolled back. HTTP does not provide (luckily) any transaction support.
A better way to approach the problem is to entirely remove the need for orchestration, this is achieved by having the gateway offloading the incoming HTTP request to add a new 'em to the cart to a queue (sending the message to itself):

public async Task Handle(string requestId, dynamic vm, RouteData routeData, HttpRequest request)
{
var requestData = new Dictionary<string, string>()
{
{ "sales-product-id", (string)routeData.Values["id"] },
{ "sales-quantity", request.Form["quantity"][0] },
};
await vm.RaiseEvent(new AddItemToCartRequested()
{
CartId = request.Cookies["cart-id"],
RequestId = requestId,
RequestData = requestData
});
await messageSession.SendLocal(new AddToCartRequest()
{
RequestId = requestId,
CartId = new Guid(request.Cookies["cart-id"]),
RequestData = requestData });
}

This means that one incoming HTTP request maps to one ongoing queue operation. And this is fine, if something goes badly at any level we can compensate. Once the message-to-self is processed it's handled by all the interested parties. For example, this is the Sales implementation:

class AddToCartRequestHandler : IHandleMessages<AddToCartRequest>
{
public Task Handle(AddToCartRequest message, IMessageHandlerContext context)
{
return context.Send("Sales.Service", new AddItemToCart()
{
RequestId = message.RequestId,
CartId = message.CartId,
ProductId = int.Parse(message.RequestData["sales-product-id"]),
Quantity = int.Parse(message.RequestData["sales-quantity"]),
});
}
}

(you can find in the repo similar implementations for all the interested services)

Now, the interesting change is that we're now dealing with an incoming message that needs to be translated into multiple outgoing messages. If something goes badly when sending one of the messages, the incoming message is retried until all the outgoing messages are successfully sent. The only downside to this is that in case of partial failure it leads to message duplication, but message deduplication can be solved at the receiver level by using the outbox pattern.

(The outbox pattern is not implemented in this demo as it's outside the scope of the demo/talk.)

Is there any downside in moving away from HTTP to messaging? Sure, it's always a tradeoff. In this case:

  • HTTP is synchronous and this has the benefit of simplifying the overall UX, when you get the HTTP response you're pretty sure the item is in the cart
  • Messaging is asynchronous and this has the benefit of entirely removing a set of problems at the UX cost. The UI now needs to deal with the option that items in the cart can be "late"

from all-our-aggregates-are-wrong-demos.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.