Package Exports
- resolve-redux
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (resolve-redux) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
🔩 resolve-redux 
This package contains utils to integrate reSolve with Redux .
📑 Table of Contents
🛠 Utils
sendCommandMiddleware
It is a Redux middleware used to send a command to the server side. It takes an object with the following field:
sendCommand
- a function used to send a command to the server side. It takescommand
and returns thePromise
object that will be resolved when the command is handled by the server. If the function is not specified, the command will be posted to/api/commands
url.
Example:
import axios from 'axios' import { createStore, applyMiddleware } from 'redux' import { sendCommandMiddleware } from 'resolve-redux' import reducer from '../reducers' const middleware = [ sendCommandMiddleware({ sendCommand: async command => axios.post(`${process.env.ROOT_DIR}/api/commands`, command) }) ] export default initialState => createStore(reducer, initialState, applyMiddleware(...middleware))
setSubscriptionMiddleware
It is a Redux middleware used to get events from
bus
. It is used withactions.setSubscription
to subscribe to required event types. It takes an object with the following field:rootDirPath
- URL where socket is placed. If URL is not specified, theprocess.env.ROOT_DIR
value or an empty string will be used. Theprocess.env.ROOT_DIR
value is passed by resolve-scripts.
Example:
import axios from 'axios' import { createStore, applyMiddleware } from 'redux' import { setSubscriptionMiddleware } from 'resolve-redux' import reducer from '../reducers' const middleware = [ setSubscriptionMiddleware({ rootDirPath: '/my-path' }) ] export default initialState => createStore(reducer, initialState, applyMiddleware(...middleware))
createReducer
Generates a standard Redux reducer from a reSolve read model. It takes two arguments:
read-model
- a reSolve read model to be converted to a Redux reducer.extendReducer
- another reducer to be combined with a new one.
This reducer includes handling of the reSolve's
merge
andreplaceState
actions.createActions
Generates Redux actions from a reSolve aggregate. This function uses the reSolve's
sendCommand
action to pass a command from Redux to the server side. Generated actions are named as aggregate's commands. This function takes two arguments:aggregate
- reSolve aggregate.extendActions
- actions to extend or redefine resulting actions.
actions
A plain object used to send special actions to be handled by other utils. It implements the following functions:
merge
Produces an action handled by a reducer which the
createReducer
function generates. It takes two arguments:readModelName
- name of a read model whose state should be updated.state
- state to merge with the existing state of the specified read model.
sendCommand
Sends a command to the server side. It takes the object with the following required arguments:
command
aggregateId
aggregateName
payload
This action is handled by
sendCommandMiddleware
automatically.setSubscription
Subscribes to new events from the server side. This function takes two arguments:
eventTypes
- array of event typesaggregateIds
- array of aggregateId
Returns an action handled by
setSubscriptionMiddleware
. It is useful to subscribe to new events from bus in real-time.replaceState
Produces an action handled by a reducer which the
createReducer
function generates. This function is very similar tomerge
, but the specified read model state is replaced with a new state instead of merging. This function takes two arguments:readModelName
- name of a read model whose state should be updated.state
- new state to replace the existing state of the specified read model.
💻 Basic Usage
How to Create Redux Store
import { createStore, applyMiddleware } from 'redux'
import axios from 'axios'
import {
createReducer,
sendCommandMiddleware,
setSubscriptionMiddleware
} from 'resolve-redux'
const aggregate = {
name: 'User',
commands: {
createUser: (state, { aggregateId, payload }) => ({
type: 'UserCreated',
aggregateId,
payload
}),
removeUser: (state, { aggregateId }) => ({
type: 'UserRemoved',
aggregateId
})
}
}
const readModel = {
name: 'Users',
initialState: [],
projection: {
UserCreated(state, event) {
return state.concat({
...event.payload,
id: event.aggregateId
})
},
UserRemoved(state, event) {
return state.filter(item =>
item.id !== event.aggregateId
)
}
}
}
const reducer = createReducer(readModel)
const store = createStore(
reducer,
readModel.initialState,
applyMiddleware(
sendCommandMiddleware({
sendCommand: command => axios.post('/api/commands', command)
}),
setSubscriptionMiddleware({
rootDirPath: process.env.ROOT_DIR
})
)
)
How to Generate Action from Aggregate
import { createActions } from 'resolve-redux'
import { connect, bindActionCreators } from 'redux'
import App from './components/App'
export const aggregate {
name: 'User',
commands: {
createUser: (state, { aggregateId, payload }) => ({
type: 'UserCreated',
aggregateId,
payload
})
}
}
export const customActions = {
deleteAll: () => ({
type: 'DELETE_ALL'
})
}
function mapDispatchToProps(dispatch) {
actions: bindActionCreators(createActions(aggregate, customActions))
}
export default connect(() => {}, mapDispatchToProps)(App)
How to Send Commands to Server
import { actions } from 'resolve-redux';
export function sendCommandAddTodoItem(aggregateId) {
return {
type: 'SEND_COMMAND_ADD_TODO_ITEM',
aggregateId,
aggregateName: 'TodoList',
payload: { name: 'todo-list' },
command: {
type: 'TodoListItemAdd',
},
};
}
store.dispatch(sendCommandAddTodoItem('aggregateId'));
// or
store.dispatch(actions.sendCommand({
aggregateId: 'aggregateId',
aggregateName: 'TodoList',
payload: { name: 'todo-list' },
command: {
type: 'TodoListItemRemove',
},
}));
🖥 Advanced Usage
Support for Optimistic Updates
const readModel = {
name: 'TodoList',
initialState: [],
projection: {
TodoListItemUpdateText(state, event) {
return state.concat({
...event.payload,
id: event.aggregateId
})
}
}
}
const reducer = createReducer(readModel, (state, action) => {
switch (action.type) {
case 'SEND_COMMAND_TODO_UPDATE_TEXT': {
if(!action.command.error) {
// Optimistic update
return state.map(item => {
if(item.id === event.aggregateId) {
return {
...item,
text: action.payload.text,
textBeforeOptimisticUpdate: item.text
}
}
})
} else {
// Revert optimistic update
return state.map(item => {
if(item.id === event.aggregateId) {
return {
...item,
text: item.textBeforeOptimisticUpdate
}
}
})
}
}
default:
return state
}
})