Application Architecture
with NgRx
WEB DEVELOPER
BRETT COFFIN
Angular
Node
FRP
Firebase
Neo4j
Kite-Surf
Speargun Fishing
Bitcoin
![](https://scontent.fnou1-1.fna.fbcdn.net/v/t1.0-9/48403986_10155772255696604_1197620766854610944_n.jpg?_nc_cat=109&_nc_oc=AQmunjiLK04JnGemkLzWxUT3Fr4gF9L2LBRRVvuRDzV8gJA5MSXezlBgikbd4ccK_1g&_nc_ht=scontent.fnou1-1.fna&oh=bf79febbab900f7aa9e1dd2fba66abb3&oe=5DEEBA94)
TECH STACK
![](https://miro.medium.com/max/1196/1*Rh5uK_EmfFwZyBTrq6AR5g.png)
FRP
DI
Decorator
Typescript
Immutability
Reducer
Store
Action (SFA)
Observable
Pattern
![](https://miro.medium.com/max/1196/1*Rh5uK_EmfFwZyBTrq6AR5g.png)
FRP
DI
Decorator
Typescript
Immutability
Reducer
Store
Action (SFA)
*
Resources
![](https://i.stack.imgur.com/1rHes.png)
![](https://cdn-images-1.medium.com/max/800/1*2K8v7kw0Nz1ncohxmil5KA.png)
![](https://miro.medium.com/max/1196/1*Rh5uK_EmfFwZyBTrq6AR5g.png)
MVC
![](https://cdn.tutsplus.com/active/uploads/legacy/flashtuts/026_PureMVC/assets/images/2.jpg)
Managing the States
Pure MVC
Implemented in many languages
Mediator,
Proxy,
Facade,
Cmd,
MVP,
MVW...
![](https://sec.ch9.ms/ch9/81bc/3283e108-a058-4ed1-a5cd-4dd4016581bc/Erik-Meijer-Rx-in-15-Minutes_960.jpg)
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
![](https://i1.wp.com/storage.googleapis.com/blog-images-backup/1*dlapmYAhWBkrFuHm020qlg.png?resize=713%2C720&ssl=1)
CQRS EVENT SOURCING
![](https://s3.amazonaws.com/media-p.slid.es/uploads/10703/images/6632337/cqrs.png)
REDUX
![](https://angularfirebase.com/images/ngrx-redux-pattern-diagram.png)
NgRx Concepts
View
Command
Effect
Event
Reducer
Snapshot
Store
Projection
![](https://miro.medium.com/max/1196/1*Rh5uK_EmfFwZyBTrq6AR5g.png)
(CLICK)
![](https://cdn-images-1.medium.com/max/2400/1*-zuNAobynKARgNRUNRUzEQ.png)
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
![](https://cdn-images-1.medium.com/max/2400/1*-zuNAobynKARgNRUNRUzEQ.png)
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
*
![](https://media1.tenor.com/images/c93c3d6a9513a0f083e36c0b6a5e2e7d/tenor.gif?itemid=5619682)
![](https://pngimage.net/wp-content/uploads/2018/05/anillo-png-2.png)
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
- 282