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, and get_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 ID of the entity 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.