Slide 1

Slide 1 text

June 15, 2017 IPT – Intellectual Products & Technologies NGRX Apps in Depth – RxJS, Reselect, Router, IndexedDB, @Effects Trayan Iliev [email protected] http://iproduct.org Copyright © 2003-2017 IPT - Intellectual Products & Technologies

Slide 2

Slide 2 text

IPT - Intellectual Products & Technologies 2 Since 2003 we provide trainings and share skills in JS/ TypeScript/ Node/ Express/ Socket.IO/ NoSQL/ Angular/ React / Java SE/ EE/ Web/ REST SOA:  Node.js + Express/ hapi + React.js + Redux + GraphQL  Angular + TypeScript + Redux (ngrx)  Java EE6/7, Spring, JSF, Portals: Liferay, GateIn  Reactive IoT with Reactor / RxJava / RxJS  SOA & Distributed Hypermedia APIs (REST)  Domain Driven Design & Reactive Microservices

Slide 3

Slide 3 text

3 NGRX Apps in Depth  State management, event sourcing, DDD, reactive programming, and stream based service architectures  Flux, Redux & NGRX: Reactive Extensions for Angular  Composing NGRX Reducers, Selectors & Middleware  Computing derived data (memoization): Reselect, RxJS  Observable (hot) streams of async actions – isolating side effects using @Effect & RxJS reactive transforms  NGRX-Router integration, Material Design, PrimeNG  Normalization/denormalization, local data – IndexedDB  Example app – code structure, lazy loading, etc.

Slide 4

Slide 4 text

Where to Find the Demo Code? 4 Angular and NGRX demos are available @GitHub: https://github.com/iproduct/course-angular  ipt-knowledge-manager – NGRX, Reselect, RxJS @Effects, modules lazy loading, AOT  angular2-change-detection-demos – modified from https://github.com/thoughtram/angular2-change- detection-demos  ngrx-example-app – NGRX official demo from https://github.com/ngrx/example-app, IndexedDB

Slide 5

Slide 5 text

Data / Event / Message Streams 5 “Conceptually, a stream is a (potentially never-ending) flow of data records, and a transformation is an operation that takes one or more streams as input, and produces one or more output streams as a result.” Apache Flink: Dataflow Programming Model

Slide 6

Slide 6 text

Data Stream Programming 6 The idea of abstracting logic from execution is hardly new -- it was the dream of SOA. And the recent emergence of microservices and containers shows that the dream still lives on. For developers, the question is whether they want to learn yet one more layer of abstraction to their coding. … there's the elusive promise of a common API to streaming engines that in theory should let you mix and match, or swap in and swap out. Tony Baer (Ovum) @ ZDNet - Apache Beam and Spark: New comopetition for squashing the Lambda Architecture?

Slide 7

Slide 7 text

Listen to Quark: 7 “Good things come in small packages” Quark – Star Trek DS9 character https://en.wikipedia.org/w/index.php?curid=12544179, Star Trek: Deep Space Nine, "Emissary", Fair use

Slide 8

Slide 8 text

Lambda Architecture - I 8 https://commons.wikimedia.org/w/index.php?curid=34963986, By Textractor - Own work, CC BY-SA 4

Slide 9

Slide 9 text

Lambda Architecture - II 9 https://commons.wikimedia.org/w/index.php?curid=34963987, By Textractor - Own work, CC BY-SA 4

Slide 10

Slide 10 text

Lambda Architecture - III 10  Data-processing architecture designed to handle massive quantities of data by using both batch- and stream-processing methods  Balances latency, throughput, fault-tolerance, big data, real-time analytics, mitigates the latencies of map- reduce  Data model with an append-only, immutable data source that serves as a system of record  Ingesting and processing timestamped events that are appended to existing events. State is determined from the natural time-based ordering of the data.

Slide 11

Slide 11 text

Druid Distributed Data Store 11 https://commons.wikimedia.org/w/index.php?curid=33899448 By Fangjin Yang - sent to me personally, GFDL Apache ZooKeeper MySQL / PostgreSQL HDFS / Amazon S3

Slide 12

Slide 12 text

Lambda Architecture: Projects - I 12

Slide 13

Slide 13 text

Direct Acyclic Graphs - DAG 13

Slide 14

Slide 14 text

14  Performance is about 2 things: – Throughput – units per second, and – Latency – response time, responsiveness - especially important from end user perspective (front-end)  Real-time – time constraint from input to response regardless of system load.  Hard real-time system if this constraint is not honored then a total system failure can occur.  Soft real-time system – low latency response with little deviation in response time  100 nano-seconds to 100 milli-seconds. [Peter Lawrey] What High Performance Means?

Slide 15

Slide 15 text

Synchronous vs. Asynchronous IO 15 DB Synchronous A A B B DB Asynchronous A B C D A B C D

Slide 16

Slide 16 text

Tracking Complexity 16 We need tools to cope with all that complexity inherent in real world applications. Simple solutions are needed – cope with problems through divide and concur on different levels of abstraction: Domain Driven Design (DDD) – back to basics: domain objects, data and logic. Described by Eric Evans in his book: Domain Driven Design: Tackling Complexity in the Heart of Software, 2004

Slide 17

Slide 17 text

Microservices and DDD 17 Main concepts:  Entities, value objects and modules  Aggregates and Aggregate Roots: value < entity < aggregate < module < BC  Aggregate Roots are exposed as Open Host Services  Hexagonal architecture : OUTSIDE <-> transformer <-> ( application <-> domain )

Slide 18

Slide 18 text

Imperative and Reactive 18 We live in a Connected Universe ... there is hypothesis that all the things in the Universe are intimately connected, and you can not change a bit without changing all. Action – Reaction principle is the essence of how Universe behaves.

Slide 19

Slide 19 text

Imperative and Reactive  Reactive Programming: using static or dynamic data flows and propagation of change Example: a := b + c  Functional Programming: evaluation of mathematical functions, ➢ Avoids changing-state and mutable data, declarative programming ➢ Side effects free => much easier to understand and predict the program behavior. Example: Observable.from(['Reactive', 'Extensions','JS']) .take(2).map(s => `${s}: on ${new Date()}`) .subscribe(s => console.log(s));

Slide 20

Slide 20 text

Functional Reactive (FRP) 20 According to Connal Elliot's (ground-breaking paper @Conference on Functional Programming, 1997), FRP is: (a) Denotative (Compositional): Observables can be composed with higher-order combinators (b) Temporally continuous (Lazy): Observables do not start emitting data until an Observer has subscribed

Slide 21

Slide 21 text

Reactive Manifesto 21 [http://www.reactivemanifesto.org]

Slide 22

Slide 22 text

22  Message Driven – asynchronous message-passing allows to establish a boundary between components that ensures loose coupling, isolation, location transparency, and provides the means to delegate errors as messages. [Reactive Manifesto] Scalable, Massively Concurrent

Slide 23

Slide 23 text

Reactive Programming Specs 23  Reactive Streams Specification [http://www.reactive-streams.org/]  ES7 Observable Spec (implemented by RxJS 5) [https://github.com/tc39/proposal-observable] Open source polyglot project ReactiveX (Reactive Extensions) [http://reactivex.io]: Rx = Observables + LINQ + Schedulers :) Java: RxJava, JavaScript: RxJS, C#: Rx.NET, Scala: RxScala, Clojure: RxClojure, C++: RxCpp, Ruby: Rx.rb, Python: RxPY, Groovy: RxGroovy, JRuby: RxJRuby, Kotlin: RxKotlin ...

Slide 24

Slide 24 text

Reactive Streams Spec. 24  Publisher – provider of potentially unbounded number of sequenced elements, according to Subscriber(s) demand. Publisher.subscribe(Subscriber) => onSubscribe onNext* (onError | onComplete)?  Subscriber – calls Subscription.request(long) to receive notifications  Subscription – one-to-one Subscriber ↔ Publisher, request data and cancel demand (allow cleanup).  Processor = Subscriber + Publisher

Slide 25

Slide 25 text

ES7 Observable Spec (RxJS 5) 25 interface Observable { constructor(subscriber : SubscriberFunction); subscribe(observer : Observer) : Subscription; subscribe(onNext : Function, onError? : Function, onComplete? : Function) : Subscription; [Symbol.observable]() : Observable; static of(...items) : Observable; static from(iterableOrObservable) : Observable; } interface Subscription { unsubscribe() : void; get closed() : Boolean; }

Slide 26

Slide 26 text

26 RxJS – JS ReactiveX (Reactive Extensions) [http://reactivex.io, https://github.com/ReactiveX]  ReactiveX is a polyglot library for composing asynchronous event streams (observable sequences).  It extends the observer pattern by declarative composition of functional transformations on events streams (e.g. map-filter- reduce, etc.)  Abstracs away low-level concerns like concurrency, synchronization, and non-blocking I/O.  Follows the next - error - completed event flow  Allows natural implementation of Redux design pattern  Alternative (together with promises) for solving “callback hell” problem

Slide 27

Slide 27 text

27 RxJS Resources RxMarbles: http://rxmarbles.com/ RxJS Coans: https://github.com/Reactive-Extensions/RxJSKoans Learn RxJS: https://www.learnrxjs.io/

Slide 28

Slide 28 text

Source: https://github.com/ReactiveX/rxjs/blob/master/doc/asset/marble-diagram-anatomy.svg, Apache v2 Anatomy of Rx Operator

Slide 29

Slide 29 text

Example: combineLatest() 29 https://projectreactor.io/core/docs/api/, Apache Software License 2.0

Slide 30

Slide 30 text

Example: switchMap() 30 http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html, Apache Software License 2.0

Slide 31

Slide 31 text

Hot and Cold Event Streams 31  PULL-based (Cold Event Streams) – Cold streams are streams that run their sequence when and if they are subscribed to. They present the sequence from the start to each subscriber.  PUSH-based (Hot Event Streams) – Hot streams emit values independent of individual subscriptions. They have their own timeline and events occur whether someone is listening or not. An example of this is mouse events. A mouse is generating events regardless of whether there is a subscription. When subscription is made observer receives current events as they happen.

Slide 32

Slide 32 text

Converting Cold to Hot Stream 32

Slide 33

Slide 33 text

Flux Design Pattern Source: Flux in GitHub, https://github.com/facebook/flux, License: BSD 3-clause "New" License

Slide 34

Slide 34 text

Linear flow: Source: @ngrx/store in GitHub, https://gist.github.com/btroncone/a6e4347326749f938510 Redux Design Pattern

Slide 35

Slide 35 text

Source: RxJava 2 API documentation, http://reactivex.io/RxJava/2.x/javadoc/ Redux == Rx Scan Opearator

Slide 36

Slide 36 text

Source: RxJS docs, https://github.com/ReactiveX/rxjs/blob/master/doc/tutorial/applications.md Redux in Plain RxJS import Immutable from 'immutable'; import someObservable from './someObservable'; import someOtherObservable from './someOtherObservable'; var initialState = { foo: 'bar' }; var state = Observable.merge( someObservable, someOtherObservable ).scan((state, changeFn) => changeFn(state), Immutable.fromJS(initialState)); export default state;

Slide 37

Slide 37 text

Source: RxJS docs, https://github.com/ReactiveX/rxjs/blob/master/doc/tutorial/applications.md Redux in Plain RxJS state.js: import Immutable from 'immutable'; import someObservable from './someObservable'; import someOtherObservable from './someOtherObservable'; var initialState = { foo: 'bar' }; var state = Observable.merge( someObservable, someOtherObservable ).scan((state, changeFn) => changeFn(state), Immutable.fromJS(initialState)); export default state;

Slide 38

Slide 38 text

Redux in Plain RxJS client.js: import state from './state'; state.subscribe(state => { document.querySelector('#text').innerHTML = state.get('foo'); }); Source: RxJS docs, https://github.com/ReactiveX/rxjs/blob/master/doc/tutorial/applications.md

Slide 39

Slide 39 text

NGRX: Reactive Extensions for Angular  core – core functionality for the ngrx platform  store – RxJS powered state management for Angular applications, inspired by Redux  router-store – bindings to connect the Angular Router to @ngrx/store  effects – side effect model for @ngrx/store  db – RxJS powered IndexedDB for Angular apps  notify – Web Notifications powered by RxJS for Angular  store-devtools, store-log-monitor - dev tools, monitoring  example-app – example app showcasing ngrx platform

Slide 40

Slide 40 text

Bootstraping NGRX App  In app.module.ts: @NgModule({ imports: [ StoreModule.provideStore(reducer), RouterStoreModule.connectRouter(), StoreDevtoolsModule.instrumentOnlyWithExtension(), EffectsModule.run(BookEffects), EffectsModule.run(CollectionEffects), DBModule.provideDB(schema), ... ] ...

Slide 41

Slide 41 text

NGRX: Defining State & Reducers  in root.reducer.ts: export interface RootState { router: fromRouter.RouterState; ui: fromUi.State; users: fromUsers.State; tests: fromTests.State; } export const reducers: ReducersMap = { router: fromRouter.routerReducer, ui: fromUi.reducer, users: fromUsers.reducer, tests: fromTests.reducer };

Slide 42

Slide 42 text

NGRX: Composing Root Reducer  in root.reducer.ts: const developmentReducer: ActionReducer = compose(storeFreeze, combineReducers)(reducers); const productionReducer: ActionReducer = combineReducers(reducers); export function rootReducer(state: any, action: any) { if (environment.production) { return productionReducer(state, action) } else { return developmentReducer(state,action) } } Store Middleware Composition Needs to be statically importable by AOT

Slide 43

Slide 43 text

NGRX Selectors  A selector function is a factory for mapping functions.  Returned function maps from larger state tree into a feature substate tree (destructing the larger state).  Selectors are used with the `select` ngrx Store operator. Following example shows selector selecting the `users` sub-state: class ClientComponent { constructor(store$: Observable) { this.usersState$ = store$.select(getUsersState); } }

Slide 44

Slide 44 text

Composing User Selectors  in users/user.selectors.ts: // User state selectors export const getEntities = (state: State) => state.entities; export const getIds = (state: State) => state.ids; export const getAll = createSelector(getEntities, getIds, (entities, ids)=>{ return ids.map(id => entities[id]); }); // Root state selectors export const getUsersState = (state: RootState) => state.users; export const getUsers = createSelector(getUsersState, getAll);

Slide 45

Slide 45 text

Using Reselect  in users/user.selectors.ts: // User state selectors export const getEntities = (state: State) => state.entities; export const getIds = (state: State) => state.ids; export const getAll = createSelector(getEntities, getIds, (entities, ids)=>{ return ids.map(id => entities[id]); }); // Root state selectors export const getUsersState = (state: RootState) => state.users; export const getUsers = createSelector(getUsersState, getAll);

Slide 46

Slide 46 text

Computing Derived Data: Reselect  Selectors can compute derived data, allowing Redux to store the minimal possible state.  Selectors are efficient. A selector is not recomputed unless one of its arguments change.  Selectors are composable. They can be used as input to other selectors.  Works correctly only when combined with immutability.

Slide 47

Slide 47 text

Computing Derived Data: Reselect import { createSelector } from 'reselect' const getVisibilityFilter = (state) => state.visibilityFilter const getTodos = (state) => state.todos export const getVisibleTodos = createSelector( [ getVisibilityFilter, getTodos ], (visibilityFilter, todos) => { switch (visibilityFilter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) }});

Slide 48

Slide 48 text

Can You Spot the Problem Here? const isFirstTodoCompleteSelector = createSelector( state => state.todos[0], todo => todo && todo.completed) export default function todos(state = initState, action) { switch (action.type) { case COMPLETE_ALL: const allMarked = state.every(todo => todo.completed) return state.map(todo => { todo.completed = !allMarked return todo }) default: return state }}

Slide 49

Slide 49 text

Can We Memoize without Reselect?  In users/components/user-list.component.ts: public ngOnInit() { this.store.dispatch(this.userActions.loadUsers()); this.subscription = this.selectedId$ .filter(id => !!id) .distinctUntilChanged() .subscribe(id => this.store.dispatch( go(['users', id])));

Slide 50

Slide 50 text

Router Integration, MD, PrimeNG

Slide 51

Slide 51 text

State Normalization / Denormalization [http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html]  When a piece of data is duplicated in several places, it becomes harder to make sure that it is updated appropriately  Nested data means that the corresponding reducer logic has to be more nested or more complex. In particular, trying to update a deeply nested field can become very ugly very fast.  Since immutable data updates require all ancestors in the state tree to be copied and updated as well, an update to a deeply nested data object could force totally unrelated UI components to re-render even if the data they're displaying hasn't actually changed.

Slide 52

Slide 52 text

Example: Users State Normalization export interface State { ids: IdentityType[]; entities: { [id: string]: User }; selectedUserId: IdentityType | null; loading: boolean; }; export const initialState: State = { ids: [], entities: {}, selectedUserId: null, loading: false }; export function usersReducer(state = initialState, action: Action): State { switch (action.type) { ...

Slide 53

Slide 53 text

IndexedDB  IndexedDB is a low-level API for client-side storage of significant amounts of structured data (key-value pairs), including files/blobs.  API uses indexes to enable high-performance searches of this data. Web Storage - useful for smaller data.  IndexedDB is built on a transactional database model – everything you do in IndexedDB always happens in the context of a transaction.  The IndexedDB API is mostly asynchronous – you have to pass a callback function.  IndexedDB uses DOM events to notify you when results are available - type prop ("success" or "error").

Slide 54

Slide 54 text

Example: @Effect and IndexedDB constructor(private actions$: Actions, private db: Database) { } @Effect({ dispatch: false }) openDB$: Observable = defer(() => { return this.db.open('books_app'); }); @Effect() loadCollection$: Observable = this.actions$ .ofType(collection.LOAD) .startWith(new collection.LoadAction()) .switchMap(() => this.db.query('books').toArray() .map((books: Book[]) => new collection.LoadSuccessAction(books)) .catch(error => of( new collection.LoadFailAction(error))) );

Slide 55

Slide 55 text

@Effect() addBookToCollection$: Observable = this.actions$ .ofType(collection.ADD_BOOK) .map((action: collection.AddBookAction) => action.payload) .mergeMap(book => this.db.insert('books', [ book ]) .map(() => new collection.AddBookSuccessAction(book)) .catch(() => of( new collection.AddBookFailAction(book))) ); Example: @Effect and IndexedDB

Slide 56

Slide 56 text

@Effect() removeBookFromCollection$: Observable = this.actions$ .ofType(collection.REMOVE_BOOK) .map((action: collection.RemoveBookAction) => action.payload) .mergeMap(book => this.db.executeWrite('books', 'delete', [book.id]) .map(() => new collection.RemoveBookSuccessAction(book)) .catch(() => of( new collection.RemoveBookFailAction(book))) ); Example: @Effect and IndexedDB

Slide 57

Slide 57 text

export interface RootState { ui: fromUi.State; router: fromRouter.RouterState; } export interface ReducersMap { [key: string]: ActionReducer; } const reducers: ReducersMap = { ui: fromUi.reducer, router: fromRouter.routerReducer }; const devProdReducers: ReducersMap = { developmentReducer: compose(storeFreeze, combineReducers)(reducers), productionReducer: combineReducers(reducers) } How to Lazy Load Reducers? - I

Slide 58

Slide 58 text

export function addReducer(name: string, reducer: ActionReducer): void { reducers[name] = reducer; devProdReducers['developmentReducer'] = compose(storeFreeze, combineReducers)(reducers); devProdReducers['productionReducer'] = combineReducers(reducers); } export function rootReducer(state: any, action: any) { if (environment.production) { return devProdReducers.productionReducer(state, action) } else { return devProdReducers.developmentReducer(state,action) } } How to Lazy Load Reducers? - II This works happily with AOT!

Slide 59

Slide 59 text

 in lazy loaded module (users/user.module.ts): @NgModule({ ... }) export class UserModule { constructor() { addReducer('users', usersReducer); } } export interface RootState extends OldRootState { users: UserState; }  Then everywhere in lazy loaded module import augmented RootState from that lazy loaded module (from users/user.module.ts, not from root.reducer.ts). Adding New Lazy Loaded Reducer

Slide 60

Slide 60 text

Where to Find the Demo Code? 60 Angular and NGRX demos are available @GitHub: https://github.com/iproduct/course-angular  ipt-knowledge-manager – NGRX, Reselect, RxJS @Effects, modules lazy loading, AOT  angular2-change-detection-demos – modified from https://github.com/thoughtram/angular2-change- detection-demos  ngrx-example-app – NGRX official demo from https://github.com/ngrx/example-app, IndexedDB

Slide 61

Slide 61 text

Thank’s for Your Attention! 61 Trayan Iliev CEO of IPT – Intellectual Products & Technologies http://iproduct.org/ http://robolearn.org/ https://github.com/iproduct https://twitter.com/trayaniliev https://www.facebook.com/IPT.EACAD https://plus.google.com/+IproductOrg