5

Introducing State Management

State management for services and actors is a centerpiece of Dapr. This chapter will illustrate how an application can transfer the responsibility of managing state to Dapr, with the ability to switch between different state store types.

These are the main topics that we will explore:

  • Managing state in Dapr
  • Stateful services in an e-commerce ordering system
  • Using Azure Cosmos DB as a state store

Most, if not all, of our services and actors in the Dapr applications we are building have data persisted as a state.

The state could be the status of a request, kept aside to be able to return additional information of a complex interaction at a later stage, or it could be the central information managed by the service, such as the quantity of the available product.

State management is equally important for a new, cloud-native solution built with Dapr and for an existing solution to which we are adding Dapr services.

An overview of state management concepts is our starting point.

Technical requirements

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

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

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

Managing state in Dapr

In a microservice architecture, state refers to the collection of information that defines the context in which the microservice operates. In this section, we will learn how state can be managed and how Dapr does it.

State, stateless, and stateful

The way that state is managed defines whether a microservice is stateful (when it takes the responsibility of persisting the state upon itself) or stateless (when the state is not in its scope of responsibility).

An example of a stateful microservice would be a shopping cart microservice that keeps the list of items in a central location (such as a database) so the customer can transparently migrate between different devices to continue their shopping experience. The shopping cart microservice could be designed by keeping the state in the host/node memory and enforcing a policy at the load balancer level to route all further interactions from the client to the original node.

Would it be a good idea, in the age of the cloud, to adopt this approach? Simply not. To gain a higher level of resiliency in modern architecture, the nodes should be considered expendable resources that can fail without impacting the solution.

A good example of a stateless microservice is a machine learning model exposed via an API, which, given a set of input parameters, would return a prediction. A microservice supporting a business activity is likely to count on a state. As the microservice should be able to scale to additional instances or restore from process or host failures, it becomes increasingly relevant to keep the state externally, for example, on a Database as a Service (DBaaS) offering, relieving our architecture of these hard-to-solve problems. The shopping cart microservice could also adopt a stateless approach by persisting the state on an external store.

With the reliable state API provided by Dapr, every microservice built with the Dapr runtime can be considered a stateless service, as it keeps its state in an external, configurable store.

Let’s explore some of the state store options Dapr offers to developers.

State stores in Dapr

A state store is a Dapr component that implements reliable store interfaces.

There are plenty of state stores in Dapr; several provide support for the major cloud platforms, such as Microsoft Azure, and database services, while many others support generic databases you could run in the cloud or on the edge.

Certification life cycle

Each component in Dapr goes through a certification life cycle, thoroughly described at https://docs.dapr.io/operations/components/certification-lifecycle/. The Alpha, Beta, and Stable certification levels show the maturity of a component, giving you the ability to make an informed decision on which component to choose for your use case.

The following are only a few of the state stores available in Dapr:

  • Azure Cosmos DB
  • Azure Blob storage
  • Azure SQL Server
  • Apache Cassandra
  • MongoDB
  • MySQL
  • PostgreSQL
  • Redis

The open and extensible capabilities of Dapr allow any party to implement additional state stores. You can find the most up-to-date list of state components in the Dapr documentation at https://docs.dapr.io/reference/components-reference/supported-state-stores/.

Multiple stores can be configured in the same hosting environment, enabling your application to retrieve, update, or delete the state as a key/value (JSON-serializable) pair.

In your local development environment, the Redis state store is configured by default, pointing to a Redis container deployed during the dapr init setup.

Each state store, depending on the database or service capabilities, may provide additional features. Let’s explore transaction support first, then concurrency, and finally consistency.

Transactions

A Dapr state store can coordinate the queries to the database, resulting from the application’s interaction with the Dapr state management building block, under a transactional context by implementing the TransactionalStore interface.

Only a subset of state stores in Dapr support transactions, such as the following ones:

  • Azure Cosmos DB
  • Azure SQL Server
  • MongoDB
  • Redis
  • PostgreSQL

A specific scenario requiring transaction support involves the actor model in Dapr. We will explore this in more depth in Chapter 8, Using Actors.

Concurrency

Dapr gives developers control over concurrency in state manipulation by returning and accepting an ETag. An ETag is metadata used to identify the version of a resource, in this case, a key/value pair in Dapr state management.

An application retrieving the state can retain the attached ETag and later re-attach it to an update request, in order to prevent overwriting a newer state that might have changed in the interim. If the ETag sent back to Dapr does not match the original one, the operation is rejected as the first write wins.

By using the Dapr C# SDK, the ETag can be managed transparently for you while dealing with state management. If no ETag is provided in the state change request, a last-write-wins approach is applied. To learn more about the Dapr approach to concurrency, please see the documentation at https://docs.dapr.io/developing-applications/building-blocks/state-management/state-management-overview/#concurrency.

If you foresee a scenario in which your service will experience concurrent service requests, with an ETag, your application can make sure unintended state overwrites are avoided.

Consistency

Consistency in state change requests can also be controlled by the application. If eventual consistency is preferred (this is the default), the state change is considered successful by Dapr as soon as the underlying state store acknowledges the write operation. If strong consistency is required instead by the application, Dapr waits for the state store to complete the write operation on all of its replicas.

Not all state stores support both eventual and strong consistency modes; Azure Cosmos DB is one of those that does. You can learn more about the performance impact of consistency in the documentation available at https://docs.microsoft.com/en-us/azure/cosmos-db/consistency-levels.

By controlling the consistency, your application code can specify, for each operation, the risks versus benefits of retrieving and persisting a state.

As we explored state store capabilities, let’s see how applications can leverage them.

Interaction with state stores

An application can directly invoke the Dapr sidecar via the http://localhost:<daprPort>/v1.0/state/<storename>/<key> HTTP call or you can leverage the abstraction offered by the SDK.

The following diagram depicts the interaction between your application and the Dapr sidecar, which, influenced by the component’s configuration, leverages a state store:

Figure 5.1 – State stores in Dapr

Figure 5.1 – State stores in Dapr

The diagram in Figure 5.1 describes the steps of a state operation in Dapr. Let’s explore them in detail:

  1. The application invokes the local URL provided by the Dapr sidecar, for example, GET http://localhost:<daprPort>/v1.0/state/shoppingcart/43, to retrieve the state.
  2. The Dapr state can be updated by an application with an HTTP POST request to the Dapr state API, http://localhost:<daprPort>/v1.0/state/shoppingcart/43. The state can also be deleted with a DELETE request to the same Dapr endpoint.
  3. The state stores available to the Dapr application are defined via .yaml files, present in a components folder, or applied to the Kubernetes configuration.

To accommodate the request, a component named shoppingcart is expected to be configured.

  1. While locally, Dapr could be using the Redis local container provided by Dapr, on Kubernetes, it could be relying on an external state such as Azure Cosmos DB. All we need is a change to the component’s .yaml files.

The key of a Dapr state uses the same format on all state stores, by combining the application ID and the state key used in the application with the <App ID>||<state key> format. Following the previous example, the key for the persisted record of the shopping cart would be shoppingcart||43.

Microservice architecture suggests keeping state, and data in general, separate. Nevertheless, with this approach, Dapr lets us use the same configured state store with different Dapr applications without the risk of key collisions. 43 in the context of shoppingcart will have a different composed key than the state record of order number 43.

The following is a YAML description of a component that uses the local Redis:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""

In the previous snippet, a component of the state.redis type has been configured to connect to the Redis local instance.

Resiliency

As we learned the basics of how an application interacts with the state store via the Dapr sidecar, it is worth briefly exploring the impact of the resiliency feature on the state management building block.

The resiliency feature allows developers and operators to configure the way Dapr applications handle transient errors. As we learned in the Resiliency section in Chapter 4, Service-to-Service Invocation, there are a few types of policies that can be applied to targets such as a state component.

Why would we want to apply a resiliency policy to a state store? An example could be to gracefully handle a transient connection error to the database, which is well described in this guide on cloud development best practices: https://docs.microsoft.com/en-us/azure/architecture/best-practices/transient-faults.

Resiliency is also important to manage the stress our Dapr application puts onto the state store.

If the database we selected for the state store of our Dapr application starts to experience errors, maybe because of excessive requests to load, how do we want to handle it? Would a constant retry policy help? It depends on many factors, but it might risk aggravating the situation by flooding the state store with repeated requests.

By using a circuit breaker policy instead, as soon as errors exceed a configured threshold, interactions are suspended for a timeout, only to fully resume after a limited number of operations have been successful.

The next section is dedicated to how a stateful service for a common e-commerce platform can be organized with Dapr.

Stateful services in an e-commerce ordering system

Moving forward with the Order-Reservation scenario introduced in the previous chapters, at this stage, we will focus on persisting the state in each Dapr application.

The following diagram anticipates the change in state management that we are going to apply to our microservices:

Figure 5.2 – Multiple state stores in Dapr

Figure 5.2 – Multiple state stores in Dapr

As you can see in Figure 5.2, the Dapr reservation-service service is going to use Redis as the state store, while order-service is going to leverage the Cosmos DB state store.

The following are the project structures used to support the order-service and reservation-service Dapr applications:

  • sample.microservice.dto.order
  • sample.microservice.order
  • sample.microservice.dto.reservation
  • sample.microservice.reservation

I decided to have Data Transfer Object (DTO) libraries that a service client can use to interact with the service itself, separate from the objects used to persist the state. This is just one of many possible approaches.

We’ll start by implementing state management in reservation-service.

Stateful reservation-service

What makes the Dapr application’s reservation-service a stateful service? The short answer is that the application keeps track of the balance quantity for items, evaluating the reservation request originating from the received orders.

order-service does not keep track of the item balance between one reservation and the next. Instead, it relies on reservation-service to manage this information and preserve it safely over time; this is what is generally expected from a stateful service.

A client interacting with the ASP.NET service endpoint would use POST http://localhost:5002/reserve to reserve the quantity of a product or GET http://localhost:5002/balance/cookie2 to retrieve the current balance of a specific product (with 5002 being the port used by ASP.NET), while a different Dapr application would invoke the local sidecar (with 5020 being the port used to reach Dapr) at POST http://localhost:5020/v1.0/invoke/reservation-service/method/reserve to ultimately reach the reservation-service Dapr application, as we learned in Chapter 4, Service-to-Service Invocation.

Regardless of the route and approach used, the important point here is that Dapr enable us to code reservation-service like a stateless service as all the complexities of managing the state are transferred to the Dapr sidecar and the configured state store.

Handling the Dapr state in ASP.NET

The DaprClient class gives our code access to the Dapr runtime by abstracting the interaction with the API endpoint exposed by the sidecar.

An instance is made available to the Controller method via dependency injection. To leverage minimal hosting in .NET 6, we need to add .AddDapr to Program.cs:

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

In the following code snippets, you will see how daprClient gives us easy access to the overall Dapr runtime including states, services, and messages.

We can examine the code in ReservationController.cs:

namespace sample.microservice.reservation.Controllers;
[ApiController]
public class ReservationController : ControllerBase
{
    public const string StoreName = "reservationstore";
    [HttpPost("reserve")]
    public async Task<ActionResult<Item>> Reserve(Item
      reservation, [FromServices] DaprClient daprClient)
    {
        var state = await daprClient.GetStateEntryAsync
          <ItemState>(StoreName, reservation.SKU);
        … changes to state …
        Console.WriteLine($"ETag {state.ETag}");
        var saved = await state.TrySaveAsync();
        if (saved != true)
        {
            Console.WriteLine("Failed to save state");
            return this.StatusCode(500);
        }
        var result = new Item() {SKU = state.Value.SKU, Quantity= state.Value.BalanceQuantity};
        Console.WriteLine($"Reservation of {result.SKU} is now
          {result.Quantity}");
        return result;
    }
… omitted …

In the previous code snippet, we have the [HttpPost("reserve")] attribute establishing a route that the Dapr runtime will leverage to invoke the ASP.NET controller.

The signature of the async Reserve method returns a result of the Task<ActionResult<Item>> type, with Item being a DTO type. The same type is accepted from clients as it is used for the input parameter.

The state is requested, explicitly in the method code, asynchronously to the Dapr sidecar with await daprClient.GetStateEntryAsync<ItemState>(StoreName, reservation.SKU), in which the two parameters are storeName, which is the name of the configured state store, and the key to look for in the state store.

The method to save the state back to the Dapr store used here is state.TrySaveAsync(), which implicitly leverages the ETag. As previously described, by passing an ETag, we take a first-write-wins approach; we can be sure that no other changes occurred to this specific state entry while processing the request.

Slightly different is the approach for the GET method implemented in ReservationController.cs:

… omitted …
    [HttpGet("balance/{state}")]
    public ActionResult<Item> Get([FromState(StoreName)]
    StateEntry<ItemState> state)
    {
        Console.WriteLine("Enter item retrieval");
        if (state.Value == null)
        {
            return this.NotFound();
        }
        var result = new Item() {SKU = state.Value.SKU,
          Quantity= state.Value.BalanceQuantity};
        Console.WriteLine($"Retrieved {result.SKU} is {result.
          Quantity}");
        return result;
    }
}

The [HttpGet("balance/{state}")] attribute influences the routing of requests to this method, and the state gets implicitly requested to Dapr via the [FromState(StoreName)] attribute of StateEntry<ItemState> state; an instance of the StateEntry type, with a value of the ItemState type, is retrieved from the state store with the key passed in with balance/{state}.

It could be the case that there is no state registered for the submitted key, which can be evaluated by checking the state.Value property for null and, eventually, returning a NotFound result back to the caller.

For the time being, the reservationstore used by reservation-service is a state store component pointing to the local Redis; just by changing the component definition, it could switch to another, completely different store option.

So far, we have learned how to use DaprClient to manage state in our Dapr applications and how to configure a state store component.

In the next section, we will use Cosmos DB as a state store option and verify the implementation from Dapr’s perspective.

Using Azure Cosmos DB as a state store

Instead of using the local Redis storage, we are going to leverage another Dapr state store option, Azure Cosmos DB, a globally distributed, multi-model database service.

The steps needed to configure the new state store are as follows:

  1. Setting up Azure Cosmos DB
  2. Configuring the state store
  3. Testing the state store
  4. Partitioning with Cosmos DB
  5. Wrapping up

The application code of reservation-store will not be changed; we will only operate at the configuration level of the state store component.

We’ll start by setting up the Azure Cosmos DB resource.

Setting up Azure Cosmos DB

To create a Cosmos DB instance on Azure, please follow this guide in the Azure documentation: https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-manage-database-account.

You should also take a look at the Dapr documentation for the same purpose: https://docs.dapr.io/developing-applications/building-blocks/state-management/query-state-store/query-cosmosdb-store/.

I created an Azure Cosmos DB account and a database named state, and then provisioned two containers, one for the reservation-service state store and a second one for the order-service state store:

Figure 5.3 – Azure Cosmos DB configured with containers

Figure 5.3 – Azure Cosmos DB configured with containers

I am using the serverless service tier of Cosmos DB, which bills only based on usage (it is the best option for a small application that doesn’t have sustained traffic).

We are now ready to configure the state store component.

Configuring the state store

The Dapr component for Cosmos DB is slightly different than the one for Redis, as you can see in the following code block:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: orderstore
spec:
  type: state.azure.cosmosdb
  version: v1
  metadata:
  - name: url
    value: https://daprstatecsdb.documents.azure.com:443/
  - name: masterKey
    value: <omitted>
  - name: database
    value: state
  - name: collection
    value: orderstate

url, database, and collection match the provisioned Cosmos DB resources.

Important note

It is highly recommended that you do not keep secrets and credentials in configuration files and that you rely on the Dapr secret management feature instead.

During the dapr run launch sequence, we can look for evidence that the configured state stores are evaluated. From the lengthy output, the following is an excerpt:

PS C:Repospractical-daprchapter05> dapr run --app-id
"order-service" --app-port "5001" --dapr-grpc-port "50010"
--dapr-http-port "5010" --components-path "./components"
-- dotnet run --project ./sample.microservice.order/
sample.microservice.order.csproj --urls="http://+:5001"

The following is the corresponding output:

Starting Dapr with id order-service. HTTP Port: 5010. gRPC Port: 50010
== DAPR == time="…" level=info msg="starting Dapr Runtime
-- version 1.1.0 -- commit 6032dc2" app_id=order-service
instance=DB-XYZ scope=dapr.runtime type=log ver=1.1.0
== DAPR == time="…" level=info msg="log level set to: info"
app_id=order-service instance=DB-XYZ scope=dapr.runtime
type=log ver=1.1.0
== DAPR == time="…" level=info msg="found component
reservationstore (state.azure.cosmosdb)" app_id=order-service
instance=DB-XYZ scope=dapr.runtime type=log ver=1.1.0
== DAPR == time="…" level=info msg="found component orderstore
(state.azure.cosmosdb)" app_id=order-service instance=DB-XYZ
scope=dapr.runtime type=log ver=1.1.0

I added --components-path "./components" to specify the location of the .yaml file’s components, otherwise, Dapr would leverage the default components with Redis.

The following information messages tell us that reservationstore (state.azure.cosmosdb) and orderstore (state.azure.cosmosdb) have been correctly applied.

To avoid any confusion, order-service is going to use the orderstore state store, while reservation-service will leverage reservationstore. The .yaml files are located in the same folder.

The following diagram depicts the configuration change we applied:

Figure 5.4 – Impact of a Dapr configurable state store

Figure 5.4 – Impact of a Dapr configurable state store

Figure 5.4 depicts the transparent change of the state store used by the reservation-service application from Redis to Azure Cosmos DB.

As we configured Cosmos DB as the state and we have confirmation that the component is loaded by Dapr, it is time to test it!

Testing the state store

With order-service and reservation-service running and state stores configured, it is time to test the services to appreciate how data is persisted on Azure Cosmos DB.

As provided in the order.test.http file, a POST command to the ASP.NET endpoint of the order-service endpoint will persist our new item in state, in addition to invoking the reservation-service application:

POST http://localhost:5001/order
content-type: application/json
{
  "CustomerCode": "Davide",
  "Date": "2022-03-19T08:47:53.1224585Z",
  "Items": [
    {
      "ProductCode": "cookie4",
      "Quantity": 7
    },
    {
      "ProductCode": "bussola1",
      "Quantity": 6
    }
  ]
}

The following GET command invokes the order-service ASP.NET endpoint, which in turn invokes the Dapr state API:

GET http://localhost:5001/order/08ec11cc-7591-4702-bb4d-
7e86787b64fe
###
GET http://localhost:5010/v1.0/state/orderstore/08ec11cc-7591-
4702-bb4d-7e86787b64fe

While the Dapr application relies on the .NET SDK to get and save the state, it is often useful to know how to check the persisted state directly by interacting with the Dapr API.

We can inspect how data is persisted as an item in Azure Cosmos DB, as shown in the following screenshot:

Figure 5.5 – Data Explorer in Azure Cosmos DB

Figure 5.5 – Data Explorer in Azure Cosmos DB

As shown in Figure 5.5, we can reach the data persisted by the Dapr state with the following steps:

  1. Open Data Explorer from the navigation pane
  2. Select the database and the relevant container
  3. Browse the items
  4. Examine the record content containing the Dapr state:
Figure 5.6 – Item in Azure Cosmos DB Data Explorer

Figure 5.6 – Item in Azure Cosmos DB Data Explorer

As shown in Figure 5.6, the state is persisted as JSON, with the main payload in the value field and the item ID being the Dapr state key composed with the <application Id>||<state key> pattern.

We learned firsthand how Cosmos DB Dapr’s state store persists our application state in the database, so let’s now investigate how the key and distribution of records are correlated.

Partitioning with Cosmos DB

Partitioning enables Azure Cosmos DB to scale individual containers in a database, as items grouped along the partition key in logical partitions are then positioned in physical partitions; it is one of the most relevant concepts that Cosmo DB adopts in terms of offering a high level of performance.

By default, the Dapr state key is used as the partitionKey value for each item in the Cosmos DB container. Considering that the Dapr state API always uses the key to read and write data from Cosmos DB, it should be a good choice in most cases.

If you need to influence the value of partitionKey, you can specify it with the metadata parameter while interacting with the state object. As an example, let’s see the following code:

var metadata = new Dictionary<string,string>();
metadata.Add("partitionKey","something_else");
await state.SaveAsync(metadata: metadata);

In cases where we modified our order-service Dapr application to adopt a different partitioning scheme, this would be the persisted document in Cosmos DB:

{
    "id": "order-service||f5a34876-2737-4e8f-aba5-
      002a4e1ab0cc",
    "value": {
        "CreatedOn": "2020-09-19T11:15:20.7463172Z",
        "UpdatedOn": "2020-09-19T11:15:20.746392Z",
        "Order": {
            "Date": "2020-09-19T08:47:53.1224585Z",
            "Id": "f5a34876-2737-4e8f-aba5-002a4e1ab0cc",
            "CustomerCode": "Davide",
            "Items": [
                {
                    "ProductCode": "cookie4",
                    "Quantity": 7
                },
                {
                    "ProductCode": "bussola1",
                    "Quantity": 6
                }
            ]
        }
    },
    "partitionKey": "something_else",
    "_rid": "h+ETAOzuFXELAAAAAAAAAA==",
    "_self": "dbs/h+ETAA==/colls/h+ETAOzuFXE=/docs/
      h+ETAOzuFXELAAAAAAAAAA==/",
    "_etag": ""1800590c-0000-0d00-0000-5f65e8480000"",
    "_attachments": "attachments/",
    "_ts": 1600514120
}

In the preceding code snippet, you can see "partitionKey": "something_else" and how it differs from the "id": "order-service||f5a34876-2737-4e8f-aba5-002a4e1ab0cc" key, being influenced by the metadata parameter in the SaveAsync method.

In most cases, the default approach to partitioning should be fine, but now you know how to control it.

Using Azure Cosmos DB as a state store for Dapr does not prevent us from using it for additional scenarios, as we will learn next.

Wrapping up

As we were able to prove, Dapr influences the data persisted in the state store with its minimalistic approach. Apart from the key/value, the payload is untouched and consumable, just like any other JSON document.

This opens up an additional scenario: what if I need to search for orders submitted to order-service that contain a specific item by ProductCode? This is not information that we can search for using the Dapr runtime, as you can see here:

SELECT c.id FROM c WHERE ARRAY_CONTAINS(c["value"]["Order"].
Items, {"ProductCode": "bussola2"}, true)

The query shown, executed on the container used as the state store by order-service, will return a list of item IDs, including the following:

[
    {
        "id": "order-service||bbc1f16a-c7e3-48c3-91fb-
          b2175acfc299"
    },
    {
        "id": "order-service||91705574-df80-4844-af5f-
          66877c436e9b"
    },
    {
        "id": "order-service||ac1e173e-fe4e-476f-b6f6-
          e9615a49f47b"
    }
]

A method in our Dapr application could perform the query directly against the Azure Cosmos DB instance using the native .NET SDK to obtain the state key from the ID. As we know, the Dapr state key is composed as <App ID>||<state key>, and we can derive the orders in the scope of our search. Also, order.id bbc1f16a-c7e3-48c3-91fb-b2175acfc299, 91705574-df80-4844-af5f-66877c436e9b, and ac1e173e-fe4e-476f-b6f6-e9615a49f47b are the ones containing the ProductCode value we were looking for.

This is not information that we could obtain with a query via the Dapr runtime to the API.

Any requirement outside the scope of the Dapr state API can be approached natively by interacting with, in this scenario, Cosmos DB.

External manipulation of the state should always be avoided, but there is nothing preventing you from reading it.

We have now completed our exploration of a powerful database, Azure Cosmos DB, used in Dapr as a state store.

Summary

In this chapter, we introduced the state management API provided by Dapr and learned how an ASP.NET service can leverage it via the .NET SDK.

We also appreciated the flexibility offered by Dapr to developers and operators in terms of configuring and modifying the state stores.

By testing the Dapr state management with local Redis and then with the cloud-based Azure Cosmos DB, we proved how easy it is not only to move the state outside of a stateful microservice but also to shift from one persistence technology to another simply via a configuration change.

In the next chapter, we will discuss a flexible and scalable approach to communicating between Dapr applications.

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

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