# Entity Store Substitute
A substitute is an alternate implementation of an interface. In this case, the entity store interface.
For more information about substitutes in general, see the useful objects user guide.
The EntityStore
module includes a substitute that allows for an entity to be arbitrarily added to the store, and that can be retrieved using the standard entity store interface.
The entity store substitute doesn't interact with the message store. It can only return entities that have been added to it. It doesn't project entities and neither reads nor writes snapshots.
The substitute has no durable I/O side effects. As such, it's an inert substitute.
Note: The meaning of "substitute" in this context refers to Liskov Substitution Principle, which describes the quality of polymorphism in object-oriented systems as objects that can legitimately replace, or substitute, each other without changing the correctness or intention of the program that uses the substitute. From the standpoint of their shared interface, substitutes are not considered either more "real" or less real than other substitutes of the same interface. In this, all substitutes are real, and no substitute is considered secondary.
# Example
class SomeHandler
dependency :store, Store
# ...
handle SomeMessage do |some_message|
some_entity_id = some_message.some_entity_id
some_entity, version = some_entity.fetch(some_entity_id, include: :version)
puts some_entity.some_attribute
puts version
end
end
handler = SomeHandler.new()
handler.store.class
# => EntityStore::Substitute
id = '123'
some_entity = SomeEntity.new()
some_entity.id = id
some_entity.some_attribute = 'Some Value'
handler.store.add(id, some_entity, 11)
handler.(some_message)
# => "Some Value"
# => 11
# Entity Store Substitute Facts
- The entity store substitute doesn't interact with the message store
- The substitute respects the public entity store API, and augments it with the capability to add entities to the store directly
- The substitute adds entities to the store's internal, in-memory cache
- Entities retrieved from the substitute store have to have been explicitly added to the store
- An entity's version can be optionally added to and retrieved from the substitute's internal, in memory cache
# EntityStore::Substitute Class
The EntityStore::Substitute
class is a concrete class from the EntityStore
library in the EntityStore
namespace.
The EntityStore::Substitute
class provides:
- The
add
method for initializing a cache record with an arbitrary entity that is not the result of projecting an event stream - Inert implementations of the entity store's
fetch
,get
, andget_version
methods that don't interact with the message store
# Add an Entity to the Store
add(id, entity, version=nil)
Returns
The cache record that was created or updated.
Parameters
Name | Description | Type |
---|---|---|
id | The ID of the entity being added | String |
entity | The entity object being added | Object |
version | The stream version of the entity added | Integer |
# Retrieving an Entity
Retrieving an entity for the substitute entity store doesn't project an entity's event stream, nor read or write snapshots.
Entities can be retrieved in one of two ways:
- Via the
fetch
method - Via the
get
method
The significant difference between the fetch
and get
methods is the return value when a non-existent entity is attempted to be retrieved. The fetch
method will return a newly-constructed instance of the store's declared entity class. The get
method will return a nil
.
It's more common in handler business logic to use the fetch
method so that the returned entity does not have to be checked for a nil
value before being used.
# Fetch
fetch(id, include: nil)
Returns
The entity that was added to the store.
If the optional include
argument is specified, data from the entity's cache record can be returned as well.
Note: The fetch
method never returns a nil
when the entity retrieved does not exist (ie: There's no entity stream with the entity's ID). When the entity does not exist, rather than returning a nil
, the fetch
method will return a newly-constructed instance of the store's declared entity class.
Parameters
Name | Description | Type |
---|---|---|
id | The ID of the entity to retrieve | String |
include | List of cache record attributes to retrieve | Symbol or Array |
# Including Cache Record Data in the Returned Values
The include
named parameter returns selected data from the entity's cache record along with the entity.
The most common use of the include
parameter is to retrieve the entity's version along with the entity.
entity, version = store.fetch(some_id, include: :version)
# Cache Record Attributes That Can Be Included
Name | Description | Type |
---|---|---|
id | The ID of the cached entity | String |
entity | The cached entity itself | Object |
version | The stream version of the cached entity at the time it was cached | Integer |
Note: All cache record attributes can be requested, but other than the id
, the entity
itself, and the version
, the other attributes will be nil
.
# The no_stream Stream Version
When an attempt is made to retrieve an entity that has not been added to the store, the symbol :no_stream
is returned as the value of version
.
entity, version = store.fetch(some_non_existant_id, include: :version)
version
# => :no_stream
The :no_stream
symbol is equivalent to a stream version of -1
.
# Get
get(id, include: nil)
The get
method is almost identical to the fetch
method.
The significant difference between the get
and fetch
methods is the return value when a non-existent entity is attempted to be retrieved. The get
method will return a nil
. Whereas the fetch
method will return a newly-constructed instance of the store's declared entity class.
The get
method isn't typically a good choice in handler business logic, as it will require a nil
check for each entity returned from the store due to the possibility that an entity returned from get
may be nil
# Retrieving an Entity's Version
In order for the version to be a value other that nil
, the version will have had to have been added along with the entity.
get_version(id)
Returns
Version of the entity identified by the id
argument, or the :no_stream
symbol if not found.
Parameters
Name | Description | Type |
---|---|---|
id | The ID of the entity whose version to retrieve | String |
# Constructing an Entity Store Substitute
The entity store substitute can be constructed in one of two ways:
- Via the constructor
- Via the
dependency
macro
# Via the Constructor
EntityStore::Substitute.build()
Returns
Instance of the EntityStore::Substitute
class.
# Via the dependency
Macro
dependency :store, SomeStore
A store declared with the dependency
macro will be initialized to the store's substitute.
class SomeHandler
dependency :store, SomeStore
# ...
end
handler = SomeHandler.new
handler.store.class
# => EntityStore::Substitute
TIP
See the useful objects user guide for background on using dependencies and their substitutes.