DomainManager

The DomainManager receives all domains mapped to the key in the constructor.

The DomainManager have following responsibilities:

  1. In constructor: Sets the store to all domain instances (it sets a getter property which return the store of the DomainManager)
  2. Provide methods to collect all reducers, sagas, defaultStates from all registered domains.
  3. Provide the method resetAllStores to reset all registered domains state.

The underneath domains cannot be altered, because it would result in an unpredictable condition. And by the way it will lose Typescript and Flow support.

Methods

Prefix Method Description
public constructor(public domains: KeyDomainMap) The constructor does following things:
1. Sets the key property of all domains to the according key which is specified in the KeyDomainMap. Notes: Later on a certain domain can accessed by domainManager.domain.<key>. To keep it more DRY the key of the passed KeyDomainMap is set as the domain key, however the domain subclass can specify a key which is used for the store when really needed.
2. Sets the store property of all domains to a getter property which returns the store property value of the DomainManager. In other word: Every domain object points automatically to the DomainStore's store.
public resetAllStates(): void Calls the Domain.prototype.resetState method on all registered domains.
public getSagas(): ActionSagaDefinitions Calls the Domain.prototype.getAllSagas on all registered domains and returns a ActionSagaDefinitions which contains all sagas which should be forked and listen to th given actionType.
Notes: Because multiple sagas can listen to the same action type this method cannot return a plain {actionType => Saga} object!
public getReducers(): DomainKeyActionReducerMap Calls the Domain.prototype.getAllReducers on all registered domains and returns a DomainKeyActionReducerMap which contains all reducers.
Notes: The returned value is {domainKey => [{actionType => Reducer}, n]} because the domainKey is required for combineReducers({ <key>: <reducer>}) later on.
In the nature of redux only one handler can exists by an action type. On domain level it throws an error if you define a reducer to an already taken action type - but it's not checking accross domains because of performance reasons. If you copy you copy&past domains don't forget to set a new action type within the new namespace.
public getDefaultStates(): DomainKeyDefaultStateMap Collect defaultState properties of all domains and return it as a mapped {domainKey => defaultState} object. This can be used for handleActions({ <actionType>: <reducer>}, <initialState>).

Properties

Prefix Property name Description
public readonly domains The KeyDomainMap which is passed in the constructor.
public store The store is the redux store which must be set after it has been created. All registered domains store property are point to this property.
public getter rootState store property must be set from outside.
Easy access the the whole store state.

Additional relevant classes/functions

Name Description
createReducer(r: DomainKeyActionReducerMap, s: DomainKeyDefaultStateMap) This is a helper method which accepts the return value of DomainManager.getReducers() and DomainManager.getDefaultStates() and create the final reducer which can be passed to createStore (redux) to create the store.
It uses:
1. handleActions (redux-actions) on every domains reducer collection to combine all reducers of a domain inclusive the defaultState
2. Combines all domain specific reducer with combineReducers (redux) to the final reducer which is then returned.
createRootSaga(ActionSagaDefinitions) This is a helper method which accepts the return value of DomainManager.getSagas() and creates a root saga which can be run by the middleware. It yield and array of takeLatest effects which are generated by the createSagaFork function.
createSagaFork(actionType: string, innerGenerator: any) This is a helper method which returns a takeLatest effect (redux-saga) which listen to the given actionType. Inside the takeLatest it do following:
1. Wrap everything in a try...catch block
2. Inside the try...catch block it uses the call effect (redux-saga) to call the passed innerGenerator with the action as argument - this is the saga generator which you defined in your domains @sagaAction/@saga decorator or handlers property. If no error is thrown it calls the action.meta.callbacks.resolve(<sagaReturnValue>) otherwise the action.meta.callbacks.reject(<error>) function (if they exists). This is required if you use dispatchSync() on a ActionInterface. If you want to build the rootSaga by your own, don't forget to do the same - otherwise the dispatchSync method will never resolve.

Types

// DomainManager constructor
type KeyDomainMap = {
    [key: string]: Domain<any>
};

// DomainManager.prototype.getSagas return value
type ActionSagaDefinitions = ActionSagaDefinition[];
interface ActionSagaDefinition {
  actionType: string,
  saga: Saga,
};

// DomainManager.prototype.getReducers() return value
type DomainKeyActionReducerMap = {[domainKey: string]: ActionReducerMap};
interface ActionReducerMap {
  [actionType: string]: Reducer<any>
};

// DomainManager.prototype.getDefaultStates() return value
type DomainKeyDefaultStateMap = {
    [domainKey: string]: any,
};

Examples

Possibilities (pseudo JS code)

import { createStore, applyMiddleware } from 'redux'
import { createSagaMiddleware } from 'redux-saga'
import { DomainManager } from 'redux-domain'

// 1. instantiate a DomainManager with all required domains
const domainManager = new DomainManager({
    'auth': new AuthDomain(),
    'user': new UserDomain(),
    'system': new SystemDomain(),
    // and so on... you can have dozens or hundred of domains.
});

// 2. use the collector methods to get all reducers, sagas and default states of all domains
const reducerMap = domainManager.getReducers();
const defaultStateMap = domainManager.getDefaultStates();
const sagaDefinitions = domainManager.getSagas();

// 3. create your whole store and dependencies (use helper methods to transform reducers/defaultStates/saga so it can be consumed directly.)
const initialState = null; // initialState is provided for example when server side rendering. If this is provided it overrides the domains defaultState.
const reducer = createReducer(reducerMap, defaultStateMap);
const rootSaga = createRootSaga(sagaDefinitions);
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer,
    initialState,
    [
        applyMiddleware(sagaMiddleware),
        // other store enhancers/middlewares
    ],
);
sagaMiddleware.run(rootSaga);

// 4. You must push back the created redux store to the DomainManager so that the domains can dispatch actions and access the state.
domainManager.store = store;

// From now on the setup is finished and you can work with your domains
domainManager.domains.auth.set({ id: 123 }).dispatch();
domainManager.domains.user.update(123, { name: 'Jakopsen' }).dispatchSync();
domainManager.domains.system.showDialog('Hi there!', true).dispatch();
domainManager.domains.auth.fetch({ id: 123 }).action; // The action object (not dispatch it)

// Other DomainManager methods
domainManager.resetStore(); // Sets the state of all domains to their defined defaultState (built-in reducer).

results matching ""

    No results matching ""