Best practices

Akka Serverless is ideally suited to the creation of small, isolated services, sometimes called Microservices. There are some patterns of Microservice architecture that are well-suited for Akka Serverless. For a more detailed discussion, see The Reactive Principles new tab or the Reactive Architecture courses from Lightbend Academy.

Service design principles

As you build and deploy services, these design principles will be useful:

Defining service boundaries

We recommend use of Domain Driven Design (DDD) to determine the best way to divide responsibility and state between services. Lightbend provides a free course on Domain Driven Designnew tab. See the information on determining service boundaries in the "Decomposing the Domain" chapter, "Identifying Bounded Contexts" section.

Choosing a state model

Value Entities offer the traditional approach to persistence—​they persist the current state as a single value. Value Entities give you the benefits of the Entity pattern in general: scalable, long-lived, addressable behaviours. Event Sourced Entities have a more advanced approach to storing the current state. Because they store state as a sequence of state-changing events, they offer extra benefits such as auditing, temporal queries to reconstruct historical state, and so on.

Isolation

Each service you create should emphasise isolation--the service should be able to stand alone.

One Entity per service

It is best to have each service deal with a single Entity type. If you have more than one Entity, it is not possible for Akka Serverless to route the data events optimally across the system.

Value Entities

For value entities, you modify the state and then trigger a save of that state. Both the JavaScript and the Java SDK have a context.updateState(newState) method for this purpose. If you change the state but do not call updateState, that state change will be lost.

Event Sourced Entities

Your service must not update its in-memory state directly as a result of a command. The handling of a command, if it results in changes being required to state, should emit events. These events will then be received, at which point the in-memory state can and should be changed in response.

When you need to read state in your service, ask yourself what events should I be listening to. When you need to write state, ask yourself what events should I be emitting. See Event Sourcing for JavaScript or Java for more information.

Inter-service communications

Services that need to communicate with other services—​whether in the same Akka Serverless project or not—​should do so asynchronously, via messages (commands or events). You should avoid situations where one service calls another via its HTTP (or other synchronous) endpoint, as this tightly couples the two services and negates the advantages of event sourcing.

Message schema migration

A design issue you should plan for is the evolution of your message schemas. Commands and events have a definition, which amounts to a schema, and this schema often needs to evolve over time. Fields get added, fields get removed, the meaning of fields may change.

How do you handle this over time as your system grows and evolves?

Protocol Buffers offers a number of facilities that support evolution and migration across versions, but it is something that must be planned and handled carefully, so new versions of services don’t have problems reading older event journals that they can’t understand.