Application Architecture
with NgRx
WEB DEVELOPER
BRETT COFFIN
Angular
Node
FRP
Firebase
Neo4j
Kite-Surf
Speargun Fishing
Bitcoin

TECH STACK

FRP
DI
Decorator
Typescript
Immutability
Reducer
Store
Action (SFA)
Observable
Pattern

FRP
DI
Decorator
Typescript
Immutability
Reducer
Store
Action (SFA)
*
Resources



MVC

Managing the States
Pure MVC
Implemented in many languages
Mediator,
Proxy,
Facade,
Cmd,
MVP,
MVW...

Functional Reactive Programing
Erik Meijer
Getters
READ / PULL
() => A
Lazy Producer
Consumer is in charge
Setters
WRITE / PUSH
A => ()
Lazy Consumer
Producer is in charge
const tradeMaxPrice$ = stratFeed$
.pipe(
skipUntil(stratState$.pipe(
pluck('order'),
filter((x) => x.type === 'buy'),
takeUntil(stratState$.pipe(
pluck('order'),
filter((x) => x.type === 'sell'),
)),
)),
scan((acc, curr: Feed) => {
return (acc > curr.price) ? acc : curr.price;
}, 0),
tap(x => {
this.programmaticCommandSubject.next({tradeMaxPrice: x});
})
);Example
FRP
Orchestrate Application State Elegantly
Perfect for Highly Reative UI (ie: real time)
No Managing state in objects
Observable Chains
REDUX

CQRS EVENT SOURCING

REDUX

NgRx Concepts
View
Command
Effect
Event
Reducer
Snapshot
Store
Projection

(CLICK)

Command: queryBooks()
VIEW
COMMAND
type: string
payload: any
error: bool
meta: any
FLUX STANDARD ACTION (FSA)
queryBooks = (payload?) => {
return this.store.dispatch(
{type: Commands.QUERY_BOOKS, payload}
);
}queryBooks()
EFFECT
queryBooks()
Success
Failure
@Effect()
queryBooks$ = this.actions$
.pipe(
ofType(Commands.QUERY_BOOKS),
switchMap(_ => this.service.queryBooks()
.pipe(
map((res) => this.events.queryBooksComplete(res)),
catchError(err => this.events.queryBooksError(err))
)));EFFECT
EVENT
EVENT
EVENT
type: string
payload: any
error: bool
meta: any
FLUX STANDARD ACTION (FSA)
queryBooksComplete = (payload) => {
return {type: Events.QUERY_BOOKS_COMPLETE, payload};
};
queryBooksError = (payload) => {
return of({type: Events.QUERY_BOOKS_COMPLETE, payload, error: true});
};queryBooksComplete
REDUCER
Event: queryBooksComplete
Snapshot: books
switch (event.type) {
case Events.QUERY_BOOKS_COMPLETE: {
return queryBooksSnapshot(state, event);
}
default: {
return state;
}
}
const queryBooksSnapshot = (state: IBook[], event): IBook[] => {
const newBooks = event.error ? [] : event.payload;
return [...state, ...newBooks];
};
REDUCER
STORE
Snapshot: books
Projection
queryBooks$(): Observable<IBook[]> {
return this.store.select('books');
}
export interface AppState {
books: IBook[];
}
export const reducers: ActionReducerMap<AppState> = {
books: fromBooks.getReducers
};STORE
Books

Projection
VIEW
books$: Observable<IBook[]> = this.bookProj.queryAll$();
<li *ngFor="let book of books$ | async">{{book.name}}</li>View
Command
Effect
Event
Reducer
Snapshot
Store
Projection
Input
Ouput
*
Unidirectional data flow
Command
Effect
Event
Command
Side Effect
Command
*
COMPLEXITY
FRP is a better solution to manage complex states
This pattern solve the same way every problem
CONCITENCY
But why ?
But why ?
View
Command
Effect
Event
Reducer
Snapshot
Store
Projection
*


One pattern to rule them all
NOTES
- Don't trigger a Command from a store mutation
- You are probably missing an Effect
- Separate Commands from Events
- Use the pattern within features modules
- Build your own API around it
ng-conf 2020: Call for Papers
By bretto
ng-conf 2020: Call for Papers
- 404