Async HTTP API and service bus

It’s been a few months already since I’ve started working for good with distributed systems using (micro)services and asynchronous processing via service bus. Many issues and question raised and one of these was how to not lose the information about commands and events being processed and even more importantly, how to notify the user once the request has completed? I’ve had to come up with some solution that seems to be sufficient (at least for now) and I’d like to share it with you.


 

Let’s assume that there’s a quite typical system which contains HTTP API, service bus and the set of (micro)services – in order to make it even easier, we will have only one type of such service called the User (micro)service. Whenever the request arrives at the API it will be passed further to the service bus and then to the instance of the User (micro)service which will finally consume it and produce some event. Everything is done fully asynchronously, which means that you can’t simply wait for the result and treat it as a synchronous API call.

One of the main problems that I encountered while designing such API fully based on the asynchronous operations (and I don’t mean by that only using the async keyword, but processing the commands in a way that would not return an immediate response to the client in a synchronous manner) was how to let the end-user know that the request has finished and e.g. resource was created, updated or deleted.

It was more than certain that I had to keep track of the requests and update their state. Then I had to expose some endpoint in the API that would return a status of such operation so that consumer would know if the request completed. The first thing I did was to return a 202 (Accepted) HTTP status code for any POST/PUT/DELETE request – these would be the commands being sent to the system. Of course, the GET requests are queries and they do not need to be processed via service bus, so I can return the result immediately.

Alright, that was the first step. However, 202 is merely a status code – how could the API consumer possibly know if the request has completed? Or where to fetch the new resource from? Since I’m a big fan of CQS I tend not to return any result (or body in that case) for methods that process commands (which are not idempotent). Still, I could take advantage of the HTTP Headers and put some valuable information here. And what would it be? I chose to create 2 custom headers: X-Operation that would have a unique endpoint to the operation (containing a status of the request) and X-Resource containing endpoint to the created or updated resource (if it would be deleted, then this header would remain empty).

I have a typical ICommand, IEvent, ICommandHandler and IEventHandler interfaces and their implementations like CreateUser (a command) and UserCreated (an event) etc. In case you’re not familiar with the command and event handler patterns, please read this article. Finally, there’s this special class for tracking the requests:

Which I’m using within my ICommand:

And the corresponding RequestId inside IEvent:

So, what’s going here? Actually, it’s much simpler than it looks. Imagine the following scenario – the requests comes to the API (some particular implementation of ICommand like mentioned before CreateUser). Then, I’m additionally creating the Request instance for this command (it’s being done internally within some core API logic, I don’t want to go into details here as it’s not really relevant).

You may include whatever you want inside the Request class. For me, the most important is Id, but I’m also keeping track of the command name, origin (what was the originally invoked URL) or resource (the unique endpoint of the resource included in the X-Resource HTTP header).
On the other hand, the IEvent contains only the id of the original request, so everything can be composed into a flow (it can be just a single command producing a single event, or much more sophisticated workflow/saga).

Surely, we want to store the request details somewhere in a database:

As you can see there’s an Operation class created for each new request which may have a different state. And it’s being updated whenever an event containing given RequestId occurs. The final state of the request (or operation in that example) depends whether the published event was successful e.g. UserCreated (then the state would be equal to completed) or not (rejected status in that case + some internal operation code like “email_in_use” in order to make it clearer what exactly happened if there was an error).

The important thing here is that you’d have the User (micro)service handling all of the commands related to the users (and publishing the events), but on top of that there would be a separate (micro)service (let’s call it an Operation service) that would subscribe to all of the available commands and requests in your system (including the ones from the other microservices) and just keep track of the Request details included in your commands and update them accordingly based on the RequestId properties within the events.

Eventually, there’s an API endpoint with the following path: /operations/{requestId} which does return the the operation details – status, code, resource URL and so on. The end-user can fetch this object in order to find out if the request already finished. However, there’s even a smarter way to do it – instead of making API consumer to pull the operation status, it’s better to push it for example via web sockets using SignalR.

It was difficult at first to switch thinking from a typical request – (synchronous) response pattern, yet once I realized that there’s a way to track all of the asynchronous requests and notify the end-user what’s happening, I enjoyed using this pattern, as it provides more flexibility and scalability while designing your API. Thus such API acts just as a gateway that has no business logic whatsoever and is not a bottleneck anymore, as you’re not obliged to return a response in a synchronous way.

12 Comments Async HTTP API and service bus

  1. Pingback: Async HTTP API and service bus - How to Code .NET

  2. Pingback: Dew Drop - January 9, 2017 (#2399) - Morning Dew

  3. Pär Dahlman

    I enjoyed reading this post. We had a similar problem that we solved by passing the session id of the caller in the headers of our messages, and once the operation completed we used SignalR to push the result to the correct client (by sending it to the client with matching session id). This way, there is no need to persist the request in a database. (We didn’t provide any pull api, but then again that feels a bit like the 90’s).

    However, my question is: are using Microsoft Service Bus (the Azure Service Bus)? In that case: why? 🙂 At the plus side, it has deep integration in Azure, but I don’t like that it is closed source and hasn’t been updated since 2013. I believe that you used RabbitMq previously, so if you’ve changed broker I’d be interested to hear about that process and lessons learned.

    Reply
    1. Piotr Gankiewicz

      Hello Pär,

      Thank you, I’m very pleased to hear that the other people like you had a similar concept of using the push service to notify the API callers about the completed request.

      I’m using RabbitMQ and have no plans to change it for now. I’m a big fan of the open source, therefore, this service bus + the library that you’ve created are part of our development stack :).

      BTW the only reason we’re using a pull API is for the “fallback” purposes. If there was any issue with web sockets or SignalR was somehow unavailable it will still work.

      Reply
  4. Mariusz

    Can You provide source (book/blogpost/anything) to this idea of creating /operations/{requestId} endpoint which solves problem of notifying user that the request has completed? I’m interested of other solutions to this problem.

    Reply
    1. Piotr Gankiewicz

      Hello Mariusz,

      Are you looking for code samples about how this works? I’m not aware of any projects, as I didn’t look for them, instead, we did implement everything on our own.

      You could take a look at our open source project that we’re developing within our Noordwind company. https://github.com/noordwind/Coolector.Api – this is the API + there are other repositories that are mostly (micro)services.

      The one that you might find interesting is this one:
      https://github.com/noordwind/Coolector.Services.Operations

      And this one:
      https://github.com/noordwind/Coolector.Services.SignalR

      On the client side, we have a Coolector.Web application built with Aurelia framework, and here’s the sample JS code for handling the operations:
      https://github.com/noordwind/Coolector.Web/blob/master/src/resources/services/operation-service.js

      Reply
      1. Mariusz

        I will definitely look on your source codes. But I thought that you get this idea from some book and beside this one idea there were more solutions to this porblem, and I just wanna know the other solutions. But you already answered my question since you came up with this solution yourself 🙂

        Reply
        1. Piotr Gankiewicz

          I did some research but couldn’t find anything that would suit my needs. However, I talked to a few people and they had similar idea to mine, therefore I gave it a try :).

          Reply
  5. Jeremy

    I have been reading about micro services and I have been dreaming of a way to implement them with an application that I am constantly working on and updating. I am a .Net developer, but the company that I am working for had me write the original code in Classic ASP. I just converted it over to use an Angular client with a WebAPI back end. It is a pretty robust application that is used as SAaS so system performance is always a concern.

    I read this post yesterday. I came back to it today because I was so excited about some of the information I learned. I have put in some research into the CQS, Command/Event patterns, and SignalR.

    Thanks for opening my eyes to a few different ideologies and technologies. Hopefully, I don’t lose too much sleep learning about all of this.

    Reply
    1. Piotr Gankiewicz

      Hi Jeremy,

      Thanks! I know a little bit a pain of working & maintaining some old tech stack. I’m happy that you like this approach – I’ve been “struggling” with (micro)services for the last few months, yet the outcome seems to be worth it. Indeed, a lot of new things and patterns to learn about.

      Goodluck with your journey :).

      Reply
  6. Mariusz

    Hi again,
    This solution works but i have some doubts about storing request info. For instance, your aurelia application sends CreateUser command, it’s processed, user is successfully created, then signalr informs aurelia that command succeded (or without signalr – aurelia after some timeout asks about command status). Done. Everything worked. But what about all that stored request info? In most cases if some kind of client sends command it’ll probably only once ask for request status. And maybe default behaviour should be that – when client asks about request status and response is that command succeded then the request info sould be deleted. I feel that when client will get info that his command succeded, he probably will never ask about that command(operation) status.

    Reply
    1. Piotr Gankiewicz

      Hi,
      If you’re referring to storing the request details in the database – well, it can be used for other purposes like auditing or simply logging what’s going on in the system. But if you don’t need this data, you can simply publish events and do not store it all :).

      Reply

Leave A Comment

Your email address will not be published. Required fields are marked *