Before developing your first service, it will help to understand how Cloudstate manages state for your services. You may be accustomed to programming for a traditional architecture where an application tier invokes a database tier to retrieve and manipulate state. This approach imposes stringent requirements on you:
to be aware of the datastore—its location and its behavior—and include logic for connecting and communicating with it.
to be responsible for handling errors associated with managing state—infrastructure level failures as well as domain level errors such as concurrent updates and transactions.
With Cloudstate, data access and error handling are the domain of the state management system. Your code does not manage database connections or transactional concerns, such as concurrent updates, caching, sharding (distributing across nodes), and routing. Cloudstate does all of that for you and supplies the state your services need at the proper time. This simplifies your code. After deployment, the way in which Akka Serverless handles state avoids contention for resources and supports scaling and failover—in short, resulting in an elastic and resilient reactive system.
To gain the benefits of Cloudstate’s data management, you need to understand its programming model. You specify service interfaces in a gRPC descriptor that defines the data types for an entity and the operations it supports. You are responsible for implementing entities.
Entities have the following attributes:
Each entity’s state is typically stored in multiple data items.
At runtime, a unique key item distinguishes each entity instance from all others. The key can be multi-part, consisting of multiple items.
Operations on entities, which come in the form of commands, can change state, that is, the data it contains.
When an entity receives a command, they already have everything they need to handle that request.
For example, you might create a
CustomerService service that manages a
Customer entity. It needs a unique identifier such as a customer
id, and will have data items such as
phone number. It has operations that can change its state, like adding or deleting a phone number or changing an address. It can refer to other entities, such as an
Invoice referring to a
Customer. And it can contain sets of other data, such as when a
Customer has multiple
phone numbers or
At runtime, Cloudstate will create and manage the lifecycle of an entity’s instances. Under the covers, Cloudstate and Akka Serverless use clustering and cluster-sharding to provide Reactive benefits for your services:
Clustering in a distributed system allows components to be aware of each other and co-operate to achieve a specific outcome. A cluster works together to provide reliability and to manage its own membership. If a member of the cluster fails, new members can be added to replace them. Cloudstate and Akka Serverless handle clustering, you get the benefits automatically.
Cluster-sharding divides data amongst members of a cluster. Cloudstate uses a key element to separate responsibility. For example, orders in a shopping system might be distributed according to the zip code of the ship-to address. All orders for 3399 would go to one specific instance of a service that handles Florida, others would go to the service that handles Montana, and so forth. Cluster-sharding, done correctly, ensures that your data is fairly evenly distributed across the various parts of your system, and not unevenly burdening only a few instances, while others sit idle. Cloudstate and Akka Serverless handle cluster-sharding for you entirely—you get the advantages of your data being local to your services without the work.
The Cloudstate and Akka Serverless architectures encourage design of a series of small services that don’t share code and can be deployed independently. This offers these benefits to your serverless functions:
They are faster and less complex to write and debug because they focus on a small set of operations, usually around a single data entity.
They simplify operational concerns and provide scalability because they can be deployed, stopped and started independently of the rest of the system, further simplifying operational concerns.
They handle variations in load gracefully. If properly designed, multiple instances of the service can be started up when necessary to support more load: For example, if your system runs on Black Friday and the shopping cart service gets super busy, you can spin up more shopping carts to handle that load without also having to start up more copies of the catalog service. When the load decreases, these extra instances can be removed again, minimizing resource usage.
They are message driven, interacting asynchronously with the rest of the world through messages (events and commands), which decouples a system’s components. If one instance, or even a whole service, fails, it is possible for the rest of the system to keep going with reduced capabilities, rather than creating a cascading failure that takes down the entire system.
A team can focus on features of a single service at a time, without worrying about what other services or teams are doing, or when they are releasing, allowing more parallel teams to focus on other services, allowing your development efforts to scale as needed.
You can upgrade services in a "rolling" fashion, where new instances are started before older instances are removed, allowing new versions to be deployed with no downtime or interruption.
Cloudstate services are event-driven, so you will want to use techniques for architectures that support that style of isolation. A technique called Event-Storming, for instance, helps develop the definition of the events your system will need as a first-class item.