6

Publish and Subscribe

Publish/Subscribe (pub/sub) is a messaging pattern supported by Distributed Application Runtime (Dapr) to enable decoupled interactions between microservices.

In the previous chapters, we examined how applications can communicate with one another via direct service-to-service invocation. In this chapter, you will learn about the benefits of the pub/sub messaging-based pattern and how to implement it in your Dapr applications.

We have the following as the main topics of this chapter:

  • Using the pub/sub pattern in Dapr
  • Using Azure Service Bus (ASB) in Dapr
  • Implementing the saga pattern

Before we delve into the implementation details of pub/sub in Dapr, an overview of the pattern is necessary.

Technical requirements

The code for this sample can be found on GitHub at https://github.com/PacktPublishing/Practical-Microservices-with-Dapr-and-.NET-Second-Edition/tree/main/chapter06.

In this chapter, the working area for scripts and code is expected to be <repository path>chapter06. In our local environment, it is C:Repospractical-daprchapter06.

Please refer to the Setting up Dapr section in Chapter 1, Introducing Dapr, for a complete guide to the tools needed to develop with Dapr and work with the samples.

Using the pub/sub pattern in Dapr

In microservice architectures, the pub/sub pattern is widely adopted to facilitate the creation of a decoupled communication channel between different parts of an application.

The sender (the publisher) of messages/events is unaware of which microservices would consume them and at which point in time they would do it.

On the receiving end (the subscriber) of this pattern, the microservice expresses an interest in the information shared as messages/events by subscribing to a specific set of information types—or topics, to use a better term. (Note that it is not forced to consume the complete stream of messages; it will be able to distill the relevant information from the rest.)

With a similar perspective, a subscriber is also not aware of the location and status of the publisher, as we can see in the following diagram:

Figure 6.1 – Pub/sub pattern

Figure 6.1 – Pub/sub pattern

In Figure 6.1, you can see the pub/sub pattern in action: services publish messages specifying the topic—this could be of interest to other services that subscribe to one or many topics.

Over time, more subscribers can be added, with no impact on the publisher.

The pub/sub pattern is often pitted against the service-to-service invocation approach; the first offers a separation in location and time between the request and its processing, while the latter offers a predictable interaction and expectation of response time.

With pub/sub, it’s more complex to implement close loops of requests/responses as it’s not meant for this style of interaction; on the contrary, the pattern enables each microservice to operate at its own pace, scaling independently and evolving autonomously.

In a service-to-service interaction, the microservices are well aware of each other as they share an agreement (or contract) on the Application Programming Interface (API) surface, and they should aim to sustain the pace of the fastest component. Being so closely coupled brings the benefit of simpler interactions with shorter request/response cycles, as there are no intermediaries.

In a discussion of the pub/sub pattern, it might be useful to disambiguate between the concepts of message and event. They both define a piece of information shared between systems, but a message is usually used to pass a request to continue processing elsewhere, while an event brings the news that something significant occurred. An event-driven architecture is centered on the consumption (and emission) of events to which a microservice reacts.

Let’s use the example common to all our samples so far—that is, our cookie-selling e-commerce site—as follows:

  • An example of an event could be the signaling of a customer accessing our e-commerce site or the expiration time of their shopping cart
  • An example of a message could be a request to compensate an activity; for example, to void the impact of an order sent from one microservice to another, previously performed via direct service invocation, asynchronously but as soon as possible

Apart from the conceptual differences and use cases, both messages and events are equally supported by the pub/sub building block of Dapr.

An important role in a pub/sub pattern is played by the messaging system, responsible for the ingestion, safe storage, consumption of messages, and interacting with publishers and subscribers over standard protocols.

The externalization of responsibilities of a messaging system—be it a hosted software package or a messaging system as a service, as commonly provided by most major cloud vendors—is a common pattern of microservice architectures.

The full list of Dapr components supporting the pub/sub brokers can be found at https://docs.dapr.io/reference/components-reference/supported-pubsub/. The following is a subset of the supported messaging systems:

  • Azure Service Bus (ASB)
  • Azure Event Hubs
  • Neural Autonomic Transport System (NATS)
  • Kafka
  • Google Cloud Platform (GCP) Pub/Sub
  • Message Queuing Telemetry Transport (MQTT)
  • RabbitMQ
  • Redis streams

By default, Dapr enables Redis as a pub/sub component, also acting as a state store, in the self-hosted hosting mode.

As a pluggable runtime, Dapr enables the configuration of multiple pub/sub components to be used by the same application.

A Dapr application or client can interact with the pub/sub component via the Dapr API. A simple POST request to the Dapr local endpoint exposed by the sidecar with the http://localhost:<daprPort>/v1.0/publish/<pubsubname>/<topic> structure would publish data to other Dapr applications subscribing to the same topic named <topic> via the <pubsubname> configured component.

In addition to the Dapr API, the Dapr .NET Software Development Kit (SDK) simplifies the publishing of messages by abstracting the interaction with the API as well as subscribing to topics and receiving messages.

The delivery of messages to subscribing Dapr applications is guaranteed at least once; the runtime takes care of all the complexities and specifics of the messaging system, with no need for additional libraries, and guarantees that the message is going to be delivered at least once to any of the Dapr application’s instances, positively replying to the Dapr API with a HyperText Transfer Protocol (HTTP) result code of 200 or without raising exceptions.

The at-least-once delivery mechanism in Dapr enables your application to have competing consumers; messages relevant to the subscribed topics will be split among all the running instances of that specific Dapr application. On the other hand, be aware that if your code needs to make sure that a message will be processed only once, it is your code’s responsibility to avoid duplicates.

It is also important to remind you of the resiliency feature in Dapr. As we learned in the Resiliency section in Chapter 4, Sevvice-to-Service Invocation, we can apply timeout, retry, and circuit broker policies to a pub/sub component. This capability can be particularly interesting to handle transient errors both in publishing and subscribing to messages. For more information on resiliency with the publish and subscribe building block, please check the Dapr documentation at https://docs.dapr.io/operations/resiliency/targets/#pubsub.

As we have just learned the basic concepts of messaging in Dapr with pub/sub, we are now prepared to apply these using ASB as a cloud message broker with Dapr in the next section.

Using Azure Service Bus (ASB) in Dapr

To introduce the pub/sub building block of Dapr with the ASB implementation, we will develop, in C#, a prototype of a collaboration between some .NET microservices.

The following figure shows what we would like to accomplish:

Figure 6.2 – Pub/sub in Dapr with ASB

Figure 6.2 – Pub/sub in Dapr with ASB

In Figure 6.2, we have the Order service interacting with the Shipping service via pub/sub; this is the portion we are going to develop.

In the chapter06 folder, you will find many projects. For now, we will focus on order-service (sample.microservice.order) and shipping-service (sample.microservice.shipping).

Important note: projects and solution

We created samples with several C# projects grouped in a solution, with separate projects for Data Transfer Objects (DTOs), a common library for constants, and so on, referenced by both the service and client projects.

Our intention was to organize the assets in a way that would be easy to consume for you. Two microservices could also agree on the JavaScript Object Notation (JSON) format exchanged between the parties and avoid DTOs and references.

To implement the pub/sub pattern, we are going to perform the following steps:

  1. Subscribing to a topic
  2. Configuring a pub/sub component
  3. Publishing to a topic
  4. Inspecting the messages

The first step to building our code is to instrument a Dapr service to subscribe to a pub/sub topic.

Subscribing to a topic

In Dapr, there are two ways for an application to subscribe to topics: declaratively and programmatically.

With the declarative approach, a .yaml formatted file must be composed to inform Dapr of the topic the sidecar has to subscribe to, the application route to invoke, and the pub/sub component to use. The declarative approach enables your application to subscribe to topics without code changes and added dependencies. See the Dapr documentation on declarative subscriptions to learn more at https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/#declarative-subscriptions.

We are going to explore how to subscribe to topics programmatically, leveraging the abstraction provided by the Dapr SDK for .NET.

We created a sample.microservice.shipping project and then referenced the DTO project via the dotnet Command-Line Interface (CLI) as follows:

PS C:Repospractical-daprchapter06> dotnet new classlib -o
"sample.microservice.dto.shipping"
PS C:Repospractical-daprchapter06> dotnet new webapi -o
"sample.microservice.shipping"  

Let’s first examine the Program.cs configuration of this ASP.NET project code, shown in the following code snippet—it will have some differences to support pub/sub:

var builder = WebApplication.CreateBuilder(args);
var jsonOpt = new JsonSerializerOptions()
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    PropertyNameCaseInsensitive = true,
};
// Add services to the container.
builder.Services.AddControllers().AddDapr(opt => opt.
  UseJsonSerializationOptions(jsonOpt));
… omitted …
app.UseCloudEvents();
app.MapControllers();
app.MapSubscribeHandler();
app.Run();

The changes to the minimal hosting configuration in Program.cs are mainly three, outlined as follows:

  • With the .AddDapr() command, we enable Dapr integration to the ASP.NET MVC.
  • The call to app.MapSubscribeHandler() registers an endpoint that the Dapr runtime in the sidecar will invoke to become aware of the topics our microservice subscribes to. Without this statement, we would not be able to receive messages from Dapr.
  • We use app.UseCloudEvents to support the CloudEvents message format, as Dapr adheres to the CloudEvents 1.0, the specification of which you can find more details at https://github.com/cloudevents/spec/tree/v1.0.

We will now move on to decorating our ASP.NET controller to subscribe to a Dapr pub/sub topic.

The following is the method signature of ShippingController.cs in the sample.microservice.shipping project:

namespace sample.microservice.shipping.Controllers;
[ApiController]
public class ShippingController : ControllerBase
{
    public const string StoreName = "shippingstore";
    public const string PubSub = "commonpubsub";
    [Topic(PubSub, Topics.OrderPreparedTopicName)]
    [HttpPost(Topics.OrderPreparedTopicName)]
    public async Task<ActionResult<Guid>> ship(Shipment
      orderShipment, [FromServices] DaprClient daprClient)
    {            
        var state = await daprClient.GetStateEntryAsync
          <ShippingState>(StoreName, orderShipment.
          OrderId.ToString());
        state.Value ??= new ShippingState() {OrderId =
          orderShipment.OrderId, ShipmentId =
            Guid.NewGuid() };
        await state.SaveAsync();
        // return shipment Id
        var result = state.Value.ShipmentId;
        Console.WriteLine($"Shipment of orderId {orderShipment.
          OrderId} completed with id {result}");
        return this.Ok();
    }
} 

By decorating the method with the [Topic(<pubsub component>, <topic name)] attribute, we instructed Dapr to subscribe to a topic in the pub/sub component and to invoke this method if a message arrives.

Before we can publish any messages, we need to configure Dapr.

Configuring a pub/sub component

Our objective is to configure a Dapr component to support the pub/sub building block.

First, we need an external messaging system. We could use the Redis stream made available by the Dapr setup, but instead we are going to create an ASB namespace and leverage it in Dapr for some good reasons (and a strong personal preference of ours).

At this stage in developing our sample, we are running in self-hosted mode. Starting from Chapter 9, Deploying to Kubernetes, we will use Kubernetes mode in Dapr. We think that reducing the breadth of changes to the Dapr mode while keeping the components constant helps focus on what is relevant. In addition, the effort of keeping Redis as a reliable messaging store in Kubernetes is significant and beyond the scope of this book.

You can find more information on ASB concepts in the documentation at https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview. You can also find detailed instructions on how to create an ASB namespace at https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-create-namespace-portal, including how to obtain the connection string we will use later in the chapter.

As developers (or operators), we do not have the responsibility of provisioning ASB topics and subscriptions, nor providing the deployment scripts. Dapr will take care of dynamically creating topics that applications will publish to and their corresponding subscriptions.

In the components folder, we will create a pubsub.yaml file with the following structure:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: commonpubsub
  namespace: default
spec:
  type: pubsub.azure.servicebus
  version: v1
  metadata:
  - name: connectionString
    value: # replace

Each Dapr component has its metadata configuration; for the pubsub.azure.servicebus component type, you can find more details at https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/.

How can we know that our service correctly registered the subscription to the topic? By launching shipping-service, we can inspect the Dapr output as follows:

PS C:Repospractical-daprchapter06> dapr run --app-id
"shipping-service" --app-port "5005" --dapr-grpc-port "50050"
--dapr-http-port "5050" --components-path "./components" --
dotnet run --project ./sample.microservice.shipping/sample.
microservice.shipping.csproj --urls="http://+:5005"
Starting Dapr with id shipping-service. HTTP Port: 5050. gRPC
Port: 50050
… omitted …
== DAPR == time="2022-03-26T19:25:40.7256112+02:00" level=info
msg="found component commonpubsub (pubsub.azure.servicebus)"
app_id=shipping-service instance=DB-XYZ scope=dapr.runtime
type=log ver=0.10.0
… omitted …
Updating metadata for app command: dotnet run --project ./
sample.microservice.shipping/sample.microservice.shipping.
csproj --urls=http://+:5005
You're up and running! Both Dapr and your app logs will appear
here. 

From the Dapr output, we can see that the commonpubsub (pubsub.azure.servicebus) component has been found and set up.

What happened to the ASB messaging system now that we defined a pub/sub component and a subscriber? The following shows what happened:

Figure 6.3 – ASB subscriptions of a topic

Figure 6.3 – ASB subscriptions of a topic

As you can see in Figure 6.3, Dapr created a topic named onorder_prepared in the ASB namespace, with a subscription named shipping-service.

The next step is to learn how to publish messages to a pub/sub topic in Dapr.

Publishing to a topic

Any Dapr application can invoke the Dapr runtime reaching the HTTP API, via the SDK or with the Dapr CLI.

We are going to see how the Dapr CLI works as follows:

dapr publish --publish-app-id shipping-service --pubsub
commonpubsub -t OnOrder_Prepared -d '{"OrderId": "6271e8f3-
f99f-4e03-98f7-6f136dbb8de8"}'

The dapr publish command lets us send data to a topic via a pub/sub component.

It’s time to verify whether the message has been received.

From the shipping-service terminal window, we can see that the message has been received and processed as follows:

== APP == Enter shipment start
== APP == Shipment of orderId 6271e8f3-f99f-4e03-98f7-
6f136dbb8de8 completed with id 53c3cc5c-0193-412b-97e9-
f82f3e0d2f80

We found evidence that the pub/sub pattern is working as expected from the service output, but how can we inspect the messages?

Inspecting the messages

By playing around with dapr publish, you should soon realize that even the added latency between your development environment and the Azure cloud is very small from a human perspective: each message sent is promptly received by the Dapr microservice.

The ability of a publisher and subscriber to operate independently is one of the major benefits of the pub/sub pattern; we will leverage this to facilitate our learning of Dapr.

While all Dapr applications are running in the local development environment, the messages can be published successfully. If you instead terminate the shipping-service Dapr application, there would not be more applications subscribing to the onorder_prepared topic.

How can we inspect the messages flowing through our pub/sub component, being ASB in this case? I suggest we manually add a subscription following the instructions at https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quickstart-topics-subscriptions-portal#create-subscriptions-to-the-topic. Let’s name the newly created subscription inspector, with the default rule that it will receive all the messages sent to the topic.

With the shipping-service subscriber application active, we can enqueue some messages by publishing with the Dapr CLI and have the chance to inspect them.

By leveraging the Service Bus Explorer feature of ASB via the Azure portal, we can see whether there are pending messages in the inspector subscription, as illustrated in the following screenshot:

Figure 6.4 – Service Bus Explorer view of a Dapr service subscription

Figure 6.4 – Service Bus Explorer view of a Dapr service subscription

As you can see from Figure 6.4, we managed to get several messages in the subscription. With the Peek function, the messages can be inspected without reading them; by selecting one of the messages, we can access the payload.

By inspecting the messages persisted into the auditing subscription, we can see the CloudEvents format being used as illustrated in the following code snippet:

{
  "traceid": "00-149c43078b8c752a90ff48e7a0626782-
      ede8db74581c4f7f-01",
  "tracestate": "",
  "data": {
    "OrderId": "6271e8f3-f99f-4e03-98f7-6f136dbb8de8"
  },
  "id": "ce25fa55-c094-4a00-b1f1-0b4ae1a40bcb",
  "datacontenttype": "application/json",
  "type": "com.dapr.event.sent",
  "pubsubname": "commonpubsub",
  "specversion": "1.0",
  "source": "shipping-service",
  "topic": "OnOrder_Prepared"
}

The information we sent via the dapr publish command is in the data field of the message payload, and thanks to the changes we applied in the Program.cs file of our ASP.NET project, our code can deal with only the relevant portion of the CloudEvents payload.

CloudEvents is a Cloud Native Computing Foundation (CNCF) specification for describing event data. It has been adopted by Dapr to format the messages exchanged via pub/sub. See https://cloudevents.io/ for more information on the specification.

We have had our first experience with the pub/sub component of Dapr. Instead of service-to-service invocation, we interacted with a Dapr application by publishing a message to a topic, relying on an external messaging system such as ASB.

In the next section, we will leverage the Dapr pub/sub building block for a more complex pattern.

Implementing a saga pattern

As we have learned about how the pub/sub pattern is implemented in Dapr, we can now apply this knowledge to building a more complex scenario, leveraging the saga design pattern for an e-commerce order system.

There are many authoritative sources that discuss saga patterns in detail; we suggest reading https://docs.microsoft.com/en-us/azure/architecture/reference-architectures/saga/saga and https://vasters.com/archive/Sagas.html, since it is not in the scope of this book to add anything to an already rich conversation on the subject.

In a microservice architecture, a business activity (for instance, processing an order) could be divided into smaller tasks, carried out by a sequence of microservices interacting with each other.

We have learned that while a microservice gains a lot of benefits by isolating its state, a consequence of this autonomy is that distributed transactions are not conceivable over disparate combinations of databases and libraries. A distributed transaction in this scenario would probably be a bad idea as the transactional context could grow in complexity over time, with more microservices being added or evolving, and have a longer duration; a microservice may not always be available to process the same activity at the same time.

To overcome this reality, a microservice should focus on its scope of responsibility, using local transactions on its own state store/database, publishing a message, or signaling an event to notify the completion of its part of the overall activity. It should also be able to compensate the overall operations with a transaction to reverse the effects on its state, in case the overall sequence of transactions (the saga) is considered failed.

In a nutshell, a saga pattern comes into play in microservice architectures to orchestrate data consistency between the many state stores.

The following diagram shows an example of a saga pattern:

Figure 6.5 – Saga pattern with Dapr

Figure 6.5 – Saga pattern with Dapr

In the context of our cookie-selling e-commerce scenario, the following is how we will structure the saga for our ordering activity, also depicted in Figure 6.5:

  • An order is submitted (likely from a web frontend) to order-service, which registers and publishes it to the OnOrder_Submitted topic. order-service does not verify the availability of items at the ordered quantity at this stage of the sequence; it is far more likely that if an item landed in the shopping cart, it is available.

order-service subscribes to many other topics that signal that, because of irreversible conditions, the order is canceled. As other microservices rely on order-service to know the status of an order, the responsibility to keep an order’s status up to date lies with order-service.

  • reservation-service subscribes to the OnOrder_Submitted topic to learn about new orders and affect the item’s available quantities before sending a message to the OnReservation_Completed topic. If any of the items’ quantities in the order could not be allocated, it compensates for the already allocated items by reversing the effect on the available quantity before publishing the message to the OnReservation_Failed topic. By subscribing to the OnCustomization_failed topic, reservation-service will attempt to compensate the available quantity for the items not already customized, which will be discarded as they cannot be sold to anyone else.
  • The other microservice, customization-service, operates in a similar fashion by publishing messages to the OnCustomization_failed topic to signal the next steps to continue or compensate. It also operates by subscribing to the OnReservation_Completed topic, informing the completion of previous steps in the saga.
  • As it would not improve this perspective on the saga pattern, for brevity, we will keep the previously created shipping-service microservice out of the scope of this sample.

There are three steps to implement the saga, listed as follows:

  1. Publishing messages to Dapr
  2. Subscribing to a Dapr topic
  3. Testing the saga pattern

The first step to build our code is to instrument order-service to publish a message to the intended topic.

Publishing messages to Dapr

After we configure each ASP.NET Web API project to support subscriber handlers and process CloudEvents (as we did in the previous section), we are ready to publish our first message via the Dapr ASP.NET SDK.

Looking at the SubmitOrder method in the OrderController.cs file of our sample.microservice.order project, it’s as simple as the following:

await daprClient.PublishEventAsync<Order>(PubSub, common.
Topics.OrderSubmittedTopicName, order);

Let’s understand the preceding code as follows:

  • The preceding code invokes the PublishEventAsync<T> method of the instance of type DaprClient that our ASP.NET controller gained access to, via Dependency Injection (DI). DaprClient is our .NET gateway to all of Dapr’s building blocks.
  • The first parameter is pubsubName. This should match the “commonpubsub” value we specified in the .yaml file we placed in the components folder. Our microservice can use multiple pub/sub components at the same time.
  • The second parameter is topicName, which will influence the routing of our messages and will translate to the ASB topic name itself.
  • The third parameter is data, the message payload.

Important note

This sample code is simple and straightforward; it considers compensating activities and simulates conditions of error in the business logic but does not offer proper exception handling. You should invest your efforts into improving this on your own, as both expected and unexpected conditions should be evaluated to decide whether it makes sense to retry, because of transient errors, or fail and compensate.

As we have just learned how to rely on the Dapr .NET SDK to publish messages to topics, we will subscribe to the appropriate pub/sub topic in the next section.

Subscribing to a Dapr topic

As we learned in a previous section, Subscribing to a topic, an ASP.NET controller method signature can be decorated with the [Topic] attribute to subscribe to messages from the topic, as illustrated in the following code snippet:

[Topic(PubSub, common.Topics.CustomizationFailedTopicName)]
[HttpPost(common.Topics.CustomizationFailedTopicName)]
public async Task<ActionResult<Guid>>
OnCustomizationFailed(OrderCustomization customization, [FromServices] DaprClient daprClient)        
{
… omitted …
}

To leverage the Dapr ASP.NET integration, the controller’s method should also have a route attribute method; this might seem counterintuitive at first, but the Dapr runtime will invoke this method once it receives a message from the topic.

An important point of attention is on the ASP.NET controller’s outcome. As documented in the Dapr documentation at https://docs.dapr.io/reference/api/pubsub_api/#expected-http-response, the HTTP result does influence how the Dapr runtime handles the message passed to the subscriber. Let’s examine a few possible outputs as follows:

  • An HTTP 200 response with an empty payload, or with a “status” key and “SUCCESS” value in a JSON payload, informs the Dapr runtime that the message has been successfully processed by the subscriber.
  • An HTTP 200 response with a JSON payload, a “status” key, and a “RETRY” value informs Dapr that the message must be retried; this is helpful if your microservice encounters a transient error.
  • Different responses can inform Dapr to log a warning or error, or to drop or retry the message.

All the microservices have been instructed to publish and subscribe to their messages; now, it’s time to test the overall scenario.

Testing the saga pattern

Via the Dapr CLI, as described in the launch.ps1 file, we can launch the three microservices in the scope of the saga implementation from different terminal windows. Once the Dapr applications have started successfully, we are ready to test the overall saga invoking order-service via any of the Dapr sidecars, as you can find in the order.test.http file, or as follows:

  1. The originating order payload evolved to include special requests for customization is as follows:
    POST http://localhost:5010/v1.0/invoke/order-service/
      method/order 
    content-type: application/json
    {
      "CustomerCode": "Davide",
      "Items": [
        … omitted …
      ],
      "SpecialRequests" : [
        {
          "CustomizationId" : "08ffffcc-7591-4702-ffff-
            fff6787bfffe",
          "Scope":
          {
            "ProductCode": "crazycookie",
            "Quantity": 1
          }
        }
      ]
    }
  2. The order is immediately submitted. The order-service microservice communicates via pub/sub with the other microservices. The outcome is illustrated in the following output:
    == APP == Submitted order 17ecdc67-880e-4f34-92cb-ed13abbd1e68

Note that for a Dapr application, or any service in more general terms, to fully leverage the pub/sub pattern in place of a service-to-service interaction, the caller application should not need immediate feedback from the called application before returning a valuable result to its clients. In this case, order-service benefits from not being slowed down by the processing time occurring in other Dapr applications.

  1. The reservation-service microservice allocates the item quantity as follows:
    == APP == Reservation in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 of rockiecookie for 4, balance 76
    == APP == Reservation in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 of bussola8 for 7, balance 1
    == APP == Reservation in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 of crazycookie for 2, balance 19
    == APP == Reservation in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 completed
  2. The customization-service microservice is ready to receive special requests for customizing the cookies. Unfortunately, the customization of the crazycookie Stock-Keeping Unit (SKU) shown in the following code snippet is almost certain to fail:
    == APP == Customization in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 of crazycookie for 1 failed
    == DAPR == time="2022-09-05T21:36:09.1056547+02:00" level=error msg="non-retriable error returned from app while processing pub/sub event … omitted …

customization-service in fact fails, and it publishes a message in OnCustomization_failed to notify the saga participants. As you can see, we received an output from the application and from Dapr as well. In this case, the customization-service code sample returned a response to inform that, while something unexpected happened, the message should not be retried as the condition of error is considered unrecoverable.

  1. reservation-service also has the goal to compensate for the failed order customization by releasing the reserved quantities for all items that have not already been customized, and therefore are still sellable.
  2. As reservation-service subscribes to the OnCustomizationFailed topic, it is ready to perform compensating actions as the following output shows:
    == APP == Reservation in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 of rockiecookie for -4, balance 80
    == APP == Reservation in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 of bussola8 for -7, balance 8
    == APP == Reservation in 17ecdc67-880e-4f34-92cb-ed13abbd1e68 of crazycookie for -1, balance 20
    == APP == Acknowledged customization failed for order 17ecdc67-880e-4f34-92cb-ed13abbd1e68

As an additional note, in the ReservationController.cs file, to compensate for the reservation on the item quantities, Language-Integrated Query (LINQ) technology has been used to calculate it. As this is not in this book’s scope, we encourage you to go read and learn more on the topic from the documentation at https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/.

  1. order-service subscribes to the OnCustomizationFailed topic too. If we look at the ASB namespace in the Azure portal, we should see two subscriptions on the OnCustomizationFailed topic.

Also, this microservice receives the following message on the customization failure:

== APP == Acknowledged customization failed for order 17ecdc67-880e-4f34-92cb-ed13abbd1e68

With this brief sequence of traces, we were able to implement and appreciate the simplicity and power of the saga pattern with Dapr.

Summary

In this chapter, we learned how to adopt the pub/sub building block of Dapr to decouple communication between microservices in a way that is far more efficient than we achieved with service-to-service direct invocation, but not without additional effort.

We figured out how to configure the pub/sub component with ASB and how to use the Dapr .NET SDK to instruct ASP.NET controllers to pub/sub messages.

Finally, we discussed the saga design pattern to tackle the complexity of distributed data consistency without resorting to distributed transactions and implemented it in our sample scenario.

In the next chapter, we will explore the resource-binding building block to interact with external services and events.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.195.76