vlingo/lattice

A compute and processing grid for actors with vlingo/lattice.

Feature Overview

The vlingo/lattice component provides tooling for reactive Domain-Driven Design projects that are highly concurrent. The tools of the component include compute grid, actor caching, spaces, cross-node cluster messaging, publish-subscribe, CQRS, and Event Sourcing support.

Feature

Description

Grid

Provides an API for distributed computations and data processing across multiple nodes in a cluster. Supports distributed parallel processing by sending computational execution requests to actors on any node in a cluster with the potential to receive results in return.

CQRS, Sourcing, and Persistence

Command-Query Responsibility Segregation uses two models per Bounded Context (application or service), the Command Model and the Query Model, otherwise known as the Read Model and the Write Model, respectively.

In addition, vlingo/lattice supports three styles of persistence, including Object, Sourcing, and Stateful.

Exchange

Provides an abstraction over Publish-Subscribe messaging exchanges or topics. This is a means to publish messages to exchanges/topics insides or outside your current application/service, and to subscribe to such messages. Provides translators from internal to external message types, and from external to internal message types.

Processes

Long-running processes that manage complex, coarse-grained, business transactions, must be managed through every step until completion. These tools assist in managing such processes whether by means of object persistence, stateful command models, or sourced with commands.

Routers

Use message-driven command routing to handlers that are responsible for modifying the domain model, which may be segregated as a Command Model.

Types

The following types are defined in the vlingo/lattice component and commonly used with domain-driven designed services.

Type

Descriptio

DomainEvent

A abstract type used to model a record of fact about an important business occurrence in the domain model

EventSourced

An abstract type used to create an Event Sourced entity that is backed by a persistent Journal

Command

An abstract type used to model a record of an imperative directive to carry out a business operation

CommandSourced

An abstract type used to create an Command Sourced entity

ObjectEntity

An abstract type used to model an entity that is backed by object-relational persistence

StatefulEntity

An abstract type used to model an entity that is backed by Key-Value persistence

Process

An interface implemented by a component that orchestrates the steps necessary to complete a composite task

Projection

An interface implemented by a component that serves to project Command Model state onto the Query Model

Projectable

An interface used to abstract the Command Model data projected onto the Query Model

Exchange

An abstraction over Publish-Subscribe messaging exchanges and topics

CommandRouter

A means of asynchronous message-driven command routing to handlers that are responsible for modifying the domain model

These types are discussed in detail throughout the subsections of this chapter.

Uncategorized Information (Requires Clean Up and Placement)

Actor-based Entities Contrasted with Plain-Object Entities (CLEAN UP)

In a typical object-based application or service that uses POJOs, for example, the lifecycle of Entities is different from that of actor-based Entities. Here we can assume that Entity and Aggregate as defined by DDD is interchangeable, as in what is described here deals with both concepts.

POJO Lifecycles

The following describes the typical lifecycle of a POJO Entity:

  • Non-existing Entity states are newly constructed and then persisted to storage

  • Preexisting Entity states are reconstituted from storage, modified, and then persisted back to the same storage

  • Across a single VM or a multi-VM cluster, there may be any number of the same Entity instances by unique identity, and thus the database must provide optimistic concurrency and detect concurrency violations via state versions

  • The object reference is released after Entity persistence and the instance garbage collected

Actor-Entity Lifecycles

The following describes the typical lifecycle of an actor-based Entity. Note that this applies across various types of Entities supported by vlingo/lattice, including Sourced<T>, StatefulEntity<T>, and ObjectEntity<T>:

  • Non-existing states are newly persisted, and persisted state is then reflected into the actor state

  • Preexisting states are possibly already in memory; if not in memory, states are reconstituted from storage; proposed changes are then persisted back to the same storage; following persistence, the state is then reflected into the actor state

  • Across a single VM or a multi-VM cluster, there is one entity instance by identity, and all requests for creation/modification will be focused on that single instance

  • The actor is retained in memory until memory constraints call for an least-recently used determination to indicate that this specific actor instance must be evicted to make room for other "hot" actors

Regarding Sourced,EventSourced, and CommandSourced designs.

I did attempt to support the constructor based approach. It leads to several very complex problems. I'll use your example to explain. (1) The MySourcedEntity constructor doesn't execute until after the Sourced constructor, so restoring state in the Sourcedconstructor is impossible unless forcing the MySourcedEntity and all other concrete entities to pass their id to the Sourced constructor. (2) Forcing 1 to work makes it extremely difficult to reconstitute the MySourcedEntity and all other concrete entities dynamically when the lattice detects a sent message but the actor is not currently in memory. (3) Even if forcing entities to pass their id to the Sourced constructor, think of what would be required to enable the following apply(...) to work. The Sourced constructor would have to make a blocking invocation to the Journal to recover state before returning to the MySourcedEntityconstructor to allow that apply(...) to work. Otherwise, returning to the MySourcedEntityconstructor immediately would enable the apply(...)to carry out prior to state recovery, or worse, simultaneously on two separate threads, doubt causing entirely crazy race conditions that would indeed be our problem to fix. (4) To avoid issues 1-3 we could pass in the existing lifecycle event stream to a constructor, but this assumes that we could safely perform an async fetch of the stream and then reconstitute the entity before another thread raced to accomplish the same, and thus at least duplicating instances in the cluster (or worse). (5) Attempting 4 would require more asynchronous components to load the stream and reconstitute the entity. This would likely lead to requiring both Application Service components and Repository components, such are found in the over-engineered N-tier architectures that vlingo is fighting against.

We could list another 2-3 undesirable outcomes of that design, but I hopefully you are already convinced that you really don't want it after all :)

One way to rethink this is that the constructor is really not a good expression of behavior and the Ubiquitous Language. Even when designing with POJOs it's generally desirable to hide the constructor behind an expressive Factory Method, such as follows:

Proposal proposal = Proposal.submitFor(client, expectations);

If you look at the vlingo-iddd-collaboration example you can see how this is done in all cases where a new EventSourced entity is being created:

https://github.com/vlingo/vlingo-examples/blob/8fbc432545555641efab01d790183c6580a985c9/vlingo-iddd-collaboration/src/main/java/com/saasovation/collaboration/model/forum/Forum.java#L24

Again inside ForumEntity: https://github.com/vlingo/vlingo-examples/blob/8fbc432545555641efab01d790183c6580a985c9/vlingo-iddd-collaboration/src/main/java/com/saasovation/collaboration/model/forum/ForumEntity.java#L57

Again inside DiscussionEntity: https://github.com/vlingo/vlingo-examples/blob/8fbc432545555641efab01d790183c6580a985c9/vlingo-iddd-collaboration/src/main/java/com/saasovation/collaboration/model/forum/DiscussionEntity.java#L39

I did since think of one possible way to make the constructor apply() work. I could cache the event(s) of the constructor's apply()to actually be performed following the completion of restore() under the control of start(). I haven't looked at it yet, but it probably would work. This would support any uses of apply()that occur prior to start(). A major caveat here is that MySourcedEntity may assume that it has no state currently (because we are in the same constructor used for initial state and pre-recovery, and the cached apply() may be wrongly performed following start() because the event(s) could be wrong given an eventually recovered state. Thus, even though I could find a way to support it, I still think it is ultimately the wrong feature to support.

The vlingo-lattice uses a LRU to evict actors from the lattice (grid) that have not been recently used. This would not happen to an actor that had just been created or reconstituted into the lattice.

Stateful Persistence

Uses simple key-value storage with relational databases and NoSql storage to persist Command Model state, and which may also be used for Query Model state.

Projections

Using the modifications to the CQRS Command Model, project the record of mutations into the Query Model by means of reactive components.

Exchange

Provides an abstraction over Publish-Subscribe messaging exchanges or topics. This is a means to publish messages to exchanges/topics insides or outside your current application/service, and to subscribe to such messages. Provides translators from internal to external message types, and from external to internal message types.

Processes

Long-running processes that manage complex, coarse-grained, business transactions, must be managed through every step until completion. These tools assist in managing such processes whether by means of object persistence, stateful command models, or sourced with commands.

Routers

Use message-driven command routing to handlers that are responsible for modifying the domain model, which may be segregated as a Command Model.