Object Storage

Using vlingo/symbio to store objects.

An ObjectStore provides the protocol for reactively persisting and reconstituting objects. Storing objects has the advantage of searching for given instances by any number of their attributes/properties, effectively as it the objects are living in memory with only the need to ask for them by means of partial state characteristics. The objects stored are typically types of Entity/Aggregate instances, which have unique identities.

In recent years object storage has been managed by a technique known as object-relational mapping (ORM), where objects are disassembled to fit into the relational table, row, and column structures. In such cases SQL expressions are used to create, read, update, and delete such objects. Using Java means that the SQL operations will be managed using through JDBC, but will often employ a mapping layer on top of that. Some well-known ORM tools are TopLink/EclipseLink, Hibernate, JPA, and others, such as Cayenne. There are other abstractions, such as Jdbi, that are not classified as an ORM, but provide worthy querying and mapping techniques that are much less invasive than ORM tools. All of these are or can be supported by a vlingo/symbio ObjectStore implementation.

Additionally, there is a vlingo/symbio ObjectStore implementation for Apache Geode. Apache Geode is an in-memory data fabric, compute grid, and distributed cache, which can hold object states.

Object Persistence Protocols

The ObjectStore protocol extends ObjectStoreReader and ObjectStoreWriter, defining a single message type in addition: close().

public interface ObjectStore extends ObjectStoreReader, ObjectStoreWriter { void close(); }

First consider the ObjectStoreWriter. It is composed of a number of variations of persist() and persistAll() message types, with differences being in the number of parameters supported in each. The richest form of the two message types follow.

<T extends PersistentObject, E> void persist( final T persistentObject, final List<Source<E>> sources, final Metadata metadata, final long updateId, final PersistResultInterest interest, final Object object); <T extends PersistentObject, E> void persistAll( final Collection<T> persistentObjects, final List<Source<E>> sources, final Metadata metadata, final long updateId, final PersistResultInterest interest, final Object object);

As noted, there are variations of these two message types that have less parameters, providing defaults to the richer message types in their implementations. The full complement of parameters and types are described next.

Parameter

Description

T persistentObject

T is a subclass of PersistentObject, which has two attributes: long persistenceId and long version, and serves as the means to persist the corresponding object with a primary key of persistenceIdand a version indicating how many times the object has be modified.

Collection<T> persistentObjects

The same as T persistentObject, but as a collection of one or more.

List<Source<E>> sources

The List of Source instances, which are concrete subclasses of DomainEvent, Command, or ProcessMessage (see vlingo/lattice for details).

Metadata metadata

The metadata to associate with the stored data.

long updateId

The identity indicating that this persistence operation is considered an update of one or more previously read objects, or -1L if it is a creation, not an update, operation. A non-negative updateId will have been provided with the result of a read query. See QueryExpression and its QueryMode, which will be either ReadOnly or ReadUpdate.

PersistResultInterest interest

A protocol backed by an Actor that will receive indication of the results following the persistence operation. During tests this protocol may be mocked as a plain object, but must be designed for concurrent access (see AccessSafely). See below for the definition of this protocol.

Object object

An object that will be sent along with the PersistResultInterest. It may be nullor a valid instance of any application (or service) defined type.

When using ObjectStoreWriter, the sender must provide an Actor or mocked test object that implements the PersistResultInterestprotocol.

public static interface PersistResultInterest {
void persistResultedIn(final Outcome<StorageException,Result> outcome, final Object persistentObject, final int possible, final int actual, final Object object);
}

Next consider the ObjectStoreReader protocol. It provides a number of query methods, with variations on required and optional parameters.

void queryAll(
final QueryExpression expression,
final QueryResultInterest interest,
final Object object);
void queryObject(
final QueryExpression expression,
final QueryResultInterest interest,
final Object object);

The difference between the two types of methods is the number of objects that should be included in the query results, either one or a one-or-more result.

Parameter

Description

QueryExpression expression

The expression used to resolve the query. See below for the definition of this protocol.

QueryResultInterest interest

A protocol backed by an Actor that will receive indication of the results following the query operation. During tests this protocol may be mocked as a plain object, but must be designed for concurrent access (see AccessSafely). See below for the definition of this protocol.

Object object

An object that will be sent along with the QueryResultInterest. It may be nullor a valid instance of any application (or service) defined type.

The QueryExpression is defined as follows. There are two extensions of the basic QueryExpression, which are ListQueryExpression and MapQueryExpression.

public class QueryExpression {
public final QueryMode mode;
public final String query;
public final Class<?> type;
...
}
public class ListQueryExpression extends QueryExpression {
public final List<?> parameters;
...
}
public class MapQueryExpression extends QueryExpression {
public final Map<String,?> parameters;
...
}

These class definitions include a number of factory methods for conveniently and expressively creating new instances of each.

The difference between these three query expression types is how parameters are supplied at runtime. The plain QueryExpression is a single parameterless expression. The parameters of a ListQueryExpression are ordered for setting as positional parameters 1, 2, 3, etc., or as a list of comma delimited parameters. The MapQueryExpression parameters are named using the String map keys, with the corresponding values to be set in the query expression, such as expression target :id matched with key "id" and value "8d8acfe97a".

When using ObjectStoreReader, the sender must provide an Actor or mocked test object that implements the QueryResultInterestprotocol.

public static interface QueryResultInterest {
void queryAllResultedIn(final Outcome<StorageException,Result> outcome, final QueryMultiResults results, final Object object);
void queryObjectResultedIn(final Outcome<StorageException,Result> outcome, final QuerySingleResult result, final Object object);
}

When the receiver is informed of a query result, it is expressed as either a multiple All result or a single Object result. Note that the Outcome may be a StorageException or a successful Result. The QueryMultiResults holds a collection of resulting objects and QuerySingleResult holds a single object result.

Application/Service Usage

Consumer implementation examples...

ObjectStore Implementations

Component: vlingo-symbio

io.vlingo.symbio.store.object.inmemory.InMemoryObjectStoreActor

Component: vlingo-symbio-geode

io.vlingo.symbio.store.object.geode.GeodeObjectStoreActor

Component: vlingo-symbio-jdbc

io.vlingo.symbio.store.object.jdbc.JDBCObjectStoreActor io.vlingo.symbio.store.object.jdbc.jdbi.JdbiObjectStoreDelegate io.vlingo.symbio.store.object.jdbc.jpa.JPAObjectStoreActor