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 or the Reactive Architecture courses from Lightbend Academy.
As you build and deploy services, these design principles will be useful:
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 Design. See the information on determining service boundaries in the "Decomposing the Domain" chapter, "Identifying Bounded Contexts" section.
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.
Each service you create should emphasise isolation--the service should be able to stand alone.
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.
context.updateState(newState) method for this purpose. If you change the state but do not call
updateState, that state change will be lost.
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.
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.
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.