The Single Mutable State is the only real design pattern that was designed
for the implementation of the domain model’s entities.
It’s optional, since the Epic infrastructure does not have any requirement in
the domain model implementation: you can use sealed classes without any
abstract base and without any attribute.
[7]
However we suggest it beyond the context it was designed for.
Indeed the Epic.Server system is based on the thread safety that this pattern provide, to allow a easy to use, transparent and cheap CQRS system. So, if you think that your entities will eventually need the features provided from the pattern (or its useful side effects), you should consider to adopt it massively on all entities that you implement.
The Single Mutable State is a behavioral pattern, directly derived from the GoF State Pattern. A plugin for your favorite IDE will be provided soon to get rid of its verbosity and to enhance your productivity. |
Decouples state transitions from command invocation to enable concurrency and simplify synchronized access to an entity that can reside in its own thread of control. [8]
A private banker and a customer share the access to an investment proposal by distinct applications. The private banker modifies the proposal while he’s talking to the customer via phone. The customer can see the proposal’s evolution on his own terminal (suppose an IPhone app) and he can accept the proposal by clicking a button. Once the proposal has been accepted the advisor can not modify it further and the his application moves to a new screen where he can send the orders to the routing system of the bank.
To enforce the business rules, the different applications need a shared access to the proposal (a well known entity in the context), but the different instances need a realtime synchronization. Moreover, the bank’s lawyers prefer consistency to availability.
The key idea in this pattern is to implement the entity’s contract with a class containing a single mutable field, the entity state. The entity delegates all requests (both queries and commands) to this state object. Such states are immutable implementations of a common base class and rappresent the different operational states; they provide explicit state transitions for the commands in the contract.
For example the abstract InvestmentProposalState
replicates the contract of the related InvestmentProposal
, but replaces
commands like void Subscribe(FundQuote)
with queries like
InvestmentProposalState Subscribe(FundQuote)
returning the new state that the
InvestmentProposal
will have. The InvestmentProposal
will works like a
proxy: it delegates to the current state all the recieved commands, it replaces
the previous mutable state with an atomic operation (a Compare And Swap
instruction) and it fires the relevant events.
This might seem complex (and it was, on the real world financial example
described before), however a simple use case is shown in the
Cargo
/ CargoState
sample provided with Epic.
Use the Single Mutable State pattern when you need a realtime synchronization between different applications running the same bounded context.
The thread safe access provided will be easy to implement and it will leave your entities simple, clean and indipendent from the infrastructure (as they only depend on the CLR).
- Entity
- implements the interface known to the clients
- maintains an instance of a ImmutableState subclass that defines the current state
- delegates all queries recieved to the current state
- delegates commands driven state transitions to the current state
- updates atomically the current state
- Entity State
- defines an interface for encapsulating the state transitions accociated with a particular sate of the Entity
- contains readonly fields only
- ImmutableState subclasses
- each subclass implements the transitions allowed from the state it represents
- contains readonly fields only
- does not reference any mutable object
- The Entity delegates all request to the ImmutableState object.
- The Entity updates its own state atomically using a Compare & Swap algoritm
- The Entity raises the relevant events when appropriate.
- Clients only know the Entity, and they don’t have to deal with its state directly.
- The concrete ImmutableStates decide which state succeeds another and under what circumstances.
Like the classic State pattern, the Single Mutable State has the following consequences:
- It localizes state specific behaviour and partitions behaviour for different states.
- It make state transitions explicit and protect the Entity from inconsistent internal states, as they happend by rebining one variable, not several.
- Entity and EntityStates are easier to test.
However, unlike the classical State pattern, the EntityState objects provide to the Entity its full state, thus it will have many instance field (at least the identifier). As a conseguence two different entities can not share their states.
Finally, in a multithread environment (like Epic.Server) serving requests from different applications, both consistency and parallelism will be guaranteed from the fine-grained locking provided from the Compare & Swap instruction. Many Entities will be able to handle commands in parallel, without any loss in the readability of the code.
The Single Mutable State pattern is easy to implement but it’s quite verbose. The main issue is to keep the Entity’s state field correctly updated and to use the right state to handle each request.
Here you can see the algoritm used on a heavily simplified real world entity.
In the motivation section a real world example from previous financial experience has been described. However the effort required to explain that domain model would overwhelm the manual.
The Epic source code is provided with the classical Evans' example implemented using the pattern described here. Look at the Cargo entity for a full example.
The classical State pattern from the Gang of Four is the noble father of the solution proposed here.
[7] Ok, I lied: pure interfaces for entities are required. But they have so many advantages beyond Epic, that they should already exist, even just to enable unit tests of the client code.
[8] We want entities that can reside in their own thread of control but that work equally well in a single threaded environment.