How To Use ReSolve View Models

Anton Zhukov
reSolve blog
Published in
3 min readFeb 27, 2018

Modern front-end JavaScript frameworks such as React provide a new way of building user interfaces by applying functional reactive programming principles to UI development.

This article introduces isomorphic and reactive UI development techniques using the React and Redux libraries and the ReSolve framework.

Basic concepts

According to the CQRS principle, the implementation should be separated into the Read Side and Write Side: the Read Side functions should never change any data. reSolve uses the following concepts for Read Side and Write Side segregation:

  • Read Model — Used to fetch the system’s state or its part in queries (mostly, GraphQL).
  • View Model — Used to show the system’s state in the UI. Works on the client side.
  • Write Model — Used to modify the system’s state.

In reSolve, the CQRS Read Side is split into Read and View Models. Since View Models provide the UI’s data, this concept is described in more detail.

ViewModel Reference Type

A View Model can be described as follows:

type ViewModel = {
name: String
projection: {
Init: Void -> State
[EventType]: (State, Event) -> State
// ...
}
serializeState: State -> String
deserializeState: String -> State
}
type State = Immutable<Object|Array> type Event = {
aggregateId: UUID
timestamp: Date
type: EventType
payload: Any
}
type EventType = String

The main part of any Read Model (a View Model is a type of Read Model) is its projection functions. These functions are used to describe the state changes in response to events. However, the Read Model does not modify the state, because according to the Event Sourcing concept, an Event Stream is stored instead of the state. The projection is used to get the state from the Event Stream (sequentially apply every single event to the initial state and return the result). Thus, the projection functions show how the state changes after every possible event.

Since the View Models are executed on the client side, the state should be transferred to the client’s browser, which means it should be readily serializable. The serialization and deserialization process is described by the serializeState and deserializeState functions. In most cases, we can use the JSON format for this.

How does it work on the client side with Redux

Subscribe/unsubscribe to/from a view model by aggregateId

The componentDidMount() is called after a component is installed. Use this method to subscribe to events. You can use this method to subscribe to events, but you should unsubscribe from events using componentWillUnmount() afterward.

The componentWillUnmount() is invoked immediately before a component is unmounted and destroyed.

To automate this process, use the connect higher-order component (HOC).

import { connect } from 'resolve-redux';const mapStateToProps = state => ({
...state[viewModelName][aggregateId],
viewModelName, // required field
aggregateId // required field
});
export default connect(mapStateToProps)(Component);

Request for Initial State

The connect handles the SUBSCRIBE action and requests an initial view model state with the specified aggregateId from the server.

Subscribe to Events

After receiving the initialState, the connect subscribes to events and sets up a web socket connection.

Unsubscribe from Events

The connect handles the UNSUBSCRIBE action, removes the state of a view model with the specified aggregateId and unsubscribes from events.

How to add createResolveMiddleware to Store

import { createStore, applyMiddleware } from 'redux';
import { createResolveMiddleware, createViewModelsReducer } from 'resolve-redux';
import viewModels from '../../common/view-models';
const reducer = createViewModelsReducer();
const middleware = [createResolveMiddleware(viewModels)];
export default initialState => createStore(reducer, initialState, applyMiddleware(...middleware));

How does it work on the server side with ReSolve

Request for the State

A query receives the current View Model’s state with the specified aggregateId. If the specified state is in the cache, it is returned instantly. Otherwise, the View Model handles all the EventStore events that have the specified aggregateId to build the state. The resulting state is stored in the cache and sent to the client.

Subscribe/Unsubscribe to/from Events

All events saved to the EventStore are also sent to the bus in real time. The client receives events if it has an active subscription to events with the specified aggregateId and eventTypes.

Code Examples

Links

--

--