Thursday, July 27, 2017

NGRX pattern in a node server

I ran into the term CQRS twice in the last while. It means Command Query Responsibility Segregation

Essentially it means that the query, or getting data be segregated from the command, or posting data. It makes sense; the performance requirements for serving data is different from what is needed to write the data. Authentication requirements are very different as well. I suspect that many large scale implementations naturally have implemented this pattern, and an acronym formalises it.

I first read about the term in a Nestjs example. Nestjs is a server application framework using express, typescript, and a very nice dependency injection system. In an episode of Angular Air one of the guests mentioned Nestjs as a pick. My app has an Express backend, and I was getting horrified of the node callback mess without typescript, and was looking for a framework. It is quite impressive.

On another great podcast at NgHouston Brandon Roberts and Mike Ryan in response to a question whether Ngrx was useful on the server mentioned the CQRS pattern.

My dream would be to write the actions and reducers in Ngrx once and use the same code on the server. The action with payload would be the message passed between the client and the server. A Store subscription would serve the get requests, and effects would handle the post requests. The dependency injection system of Nestjs would make much of the initialization and structure of Ngrx portable.

And indeed that is the case. Getting a working store with selectors returning data, and effects posting the modifications was not that difficult.

https://github.com/derekkite/ngrx-nest is where the skeleton of the endeavor resides. Right now it isn't a working example, but more an illustration of how to do things. And there are things to fix.



NGRX Platform, redux

I have been using something similar undoables for an undo/redo function in my reducers. This implementation maintains the last array in memory, which would consume quite a few resources, so I rewrote it to keep the changed documents available for redo, and implemented effects to update the persistent storage. Worked fine in ngrx/store v2. And in ngrx/store v4 if I didn't use AOT. The feared ErrorError encountered resolving symbol values statically.

In my reducer definition I had code that looked like this:

export const reducers: ActionReducerMap = {
    connection: fromConnections.reducer,
    contactlist: undoable(fromContactList.reducer),
    contactemails: undoable(fromContactEmail.reducer),
    contactphones: undoable(fromContactPhone.reducer),
    contactwebsite: undoable(fromContactWebsite.reducer),
};

undoable gets a reducer as a parameter and returns it. AOT doesn't like it.

So there are two solutions.

You can provide the reducers.

export const REDUCER_TOKEN = new InjectionToken>('Registered Reducers');

export const reducerProvider = [
    { provide: REDUCER_TOKEN, useValue: reducers },
];

creates a token and provider, then in your module some magic.

// WORKAROUND HERE
Object.assign(REDUCER_TOKEN, reducers);
// WORKAROUND HERE

Then import the StoreModule

imports: [
        BrowserModule,
        CommonModule,
      ...
        StoreModule.forRoot(REDUCER_TOKEN),
],
providers: [
        reducerProvider,
]

The Object.assign hack is necessary, it won't work without it, at least in v 4.0.1.

The second way is to

export function undoableLayoutReducer(state: any, action: Action): fromLayout.State {
    return undoable(fromLayout.reducer)(state, action);
}

export const reducers: ActionReducerMap = {
    layout: undoableLayoutReducer,
};

which encapsulates the factory function in something that AOT likes. Right now this is required if you have a factory for your reducer in a feature store, as the forFeature doesn't as yet work with providers.




Saturday, July 22, 2017

Ngrx Platform

I've used ngrx/store and ngrx/effects for a while, and like the pattern. The new version has been released, and here are some of the changes I had to make in my code.

Pre v4 you built your reducers as follows, passing an object with

stateslice: reducerfunction.

const productionReducer = combineReducers(reducers);
export function reducers(state: any, action: any) {
    return productionReducer(state, action);}

With the new version, don't use combineReducers. I was running into the situation where the reducers weren't being initialized and the state was null.

Now you create your object of reducers:

export const reducer: ActionReducerMap = {
contactprimary: fromPrimary.reducer
contactselection: fromSelectionList.reducer
contactlist: undoable(fromContactList.reducer),
relations: relationUndoable(fromRelations.reducer),...

and in your app.module imports: [] section


StoreModule.forRoot(reducer),

The second big change is the Action type has been redefined.

export interface Action {
  type: string;
}

This breaks any of your code where you use action.payload. To fix it you have to define Action in each reducer. First define your action classes.

import {Action} from "@ngrx/store";
import {PrintItem} from "../../../models/print.model";


export const ADD_TO_QUEUE = '[Printing] Add to queue';
export const REMOVE_FROM_QUEUE = '[Printing] Remove from queue';
export const MARK_STATUS = '[Printing] mark status';

export class Print implements Action {
    type = ADD_TO_QUEUE;    
constructor(public payload: PrintItem) {}
}

export class PrintDone implements Action {
    type = REMOVE_FROM_QUEUE;    
constructor(public payload: PrintItem) {}
}

export class PrintStatus implements Action {
    type = MARK_STATUS;    
constructor(public payload: PrintItem) {}
}

export type Actions
    = Print
    | PrintStatus
    | PrintDone;


Note that the payload has a type in each one, and it is the same. You can call it whatever you like, or have multiple properties. The type Actions is exported.

Then in your reducer.

import {PrintState} from "../../../models/print.model";import {ActionReducer} from "@ngrx/store";import * as printingActionTypes from "../actions/printing.actions";

export type Action = printingActionTypes.Actions;
const initialState: PrintState = {
    queue: []
};
export const reducer: ActionReducer = (
    state = initialState, action: Action) => {
    switch (action.type) {
....


In this reducer, action is of the type defined in your action classes. The same action needs to be defined in the Effects as well.

This requires refactoring much of your reducers and effects. If you have different payloads for each of your defined action classes, lots of changes.

The advantage is strict typing of the data being passed around. It caught a couple minor flaws in my code, and I will be rewriting some reducers to take advantage of the added protection.

Otherwise it all works. The documentation is very helpful, especially with the specific initialization requirements.

I will update this blog after I have refactored the feature module reducers. There are also some improvements in the way selectors can be grouped.

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]