Durable Futures
Introduction
Durable Futures are a concept I've come up with to address the complexity inherent to a distributed, event-based architecture.
The concepts in this article are covered in Season 3 of the MassTransit video series on YouTube.
The code exploring the concepts is available on GitHub.
Request, Response
One of the most understood concepts in software development is request/response. In the simplest form, call/return, this conversation pattern between a client and a service, is the most commonly used idiom in software development.
var response = service.Method(request);
As programming languages have evolved, along with the common use of asynchronous programming models, remote procedure calls (RPC) via HTTP and other protocols, and message-based systems, the most understood pattern continues to be request/response.
HTTP Client
var responseMessage = await httpClient.GetAsync();
MassTransit Request Client
var response = await client.GetResponse<TResponse>(new Request());
In each of these examples, await is a key enabler. Requests are sent asynchronously over a network connection to the remote service that produces a response which is then delivered to the client.
With HTTP, a connection is maintained by the client on which the response is sent. With MassTransit, a requestId and responseAddress passed to the service are used to send the response which is then read from the queue by the client bus and correlated back to the request.
Task
The return type, Task<T>
, is a C# language feature that represents a future. It's a reference type which means it is only accessible by reference. Since Task<T>
is a future, it promises to deliver at some point:
T
(completed)- An exception (faulted)
- A task canceled exception (canceled)
I'm intentionally ignoring ValueTask<T>
for now. It behaves similarly but has a few restrictions, one of which being that it should only be evaluated once.
async
and await
, writing asynchronous code was significantly more complex. Continuation passing was commonly used, resulting in deeply nested code that was difficult to understand and even more difficult to debug. Without a doubt, async
and await
are two of the best keywords in C#.