Angular 2+
Architecture for scalable Angular applications
Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
Angular 2+
Architecture for scalable Angular applications
Scalable architecture for Angular applications
Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
Agenda
• @AboutMe()
• Paradigms
• Promise & array
• Marbles
• Pattern
• Achitecture
• Q&A
Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
Agenda
• @AboutMe()
• Paradigms
• Promise & array
• Marbles
• Pattern
• Achitecture
• Q&A
Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
Mysterious, but get ready for a paradigm shift
@AboutMe()
• I started programming 18 years ago
• I code for money from the 9 years
• I code for Decerto 3 years
• I was programming in Pascal, C, PHP, Javascript, Bash, SQL, C++, Java,
Typescript
• I did some AJAX before it was popular
• I did some UX before it was so popular
• I did some Devops before it was so named
• I was programming SPAs over the last 4 years in AngularJS
• I observe the development of Angular 2 since AngularJS 1.5-beta.0
• I am doing SPA in Angular 2 & 4 the last few months
Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
@AboutMe()
• I started programming 18 years ago
• I code for money from the 9 years
• I code for Decerto 3 years
• I was programming in Pascal, C, PHP, Javascript, Bash, SQL, C++, Java,
Typescript
• I did some AJAX before it was popular
• I did some UX before it was so popular
• I did some Devops before it was so named
• I was programming SPAs over the last 4 years in AngularJS
• I observe the development of Angular 2 since AngularJS 1.5-beta.0
• I am doing SPA in Angular 2 & 4 the last few months
Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
Paradigms:
imperative vs declarative
wykonywanie obliczeń w pętli
for vs forEach
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
// for
for (let i = 0; i < array.length; ++i) {
console.log(array[i]);
}
// forEach
array.forEach((item) => console.log(item));
for vs map
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
// for
const doubled1 = [];
for (let i = 0; i < array.length; ++i) {
let double = array[i] * 2;
doubled1.push(double);
}
// map
const doubled2 = array.map((item) => 2 * item);
for vs filter
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
const isPrime = (n) => ...; // true or false
// for
const primes1 = [];
for (let i = 0; i < array.length; ++i) {
if(isPrime(array[i])){
primes1.push(array[i]);
}
}
// filter
const primes2 = array.filter(isPrime);
for vs reduce
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
// for
let sum1 = 0;
for (let i = 0; i < array.length; ++i) {
sum1 = sum1 + array[i];
}
// reduce
const sum2 = array.reduce((last, item) => last + item, 0);
for vs reduce
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
const add = (a, b) => a + b;
// for
let sum1 = 0;
for (let i = 0; i < array.length; ++i) {
sum1 = add(sum1, array[i]);
}
// reduce
const sum2 = array.reduce(add, 0);
map-filter-reduce
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
const double = (x) => 2 * x;
const isPrime = (n) => ...;
const add = (a, b) => a + b;
const doubled2 = array.map(double);
const primes2 = array.filter(isPrime);
const sum2 = array.reduce(add, 0);
map-filter-reduce
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
const double = (x) => 2 * x;
const isPrime = (n) => ...;
const add = (a, b) => a + b;
const doubled2 = array.map(double);
const primes2 = array.filter(isPrime);
const sum2 = array.reduce(add, 0);
Primitive value, array, promise, …
…
Primitive value, array, promise, …
number
Array
<number>
Promise
<number>
?
Single Multiple
Now
In future
Primitive value, array, promise, …
number
Array
<number>
Promise
<number>
?
Single Multiple
Now
In future
Primitive value, array, promise, …
number
Array
<number>
Promise
<number>
?
Single Multiple
Now
In future
Primitive value, array, promise, …
number
Array
<number>
Promise
<number>
Observable
<number>
Single Multiple
Now
In future
promise vs observable
promise
.then( (value) => console.log(value))
.catch( (reason) => console.error(reason))
.finally(() => console.log('Done'));
observable.subscribe(
(value) => console.log(value),
(reason) => console.error(reason),
() => console.log('Done')
);
promise vs observable
promise
.then( (value) => console.log(value))
;
observable.subscribe(
(value) => console.log(value)
);
promise vs observable
promise
.then((value) => console.log(value));
observable
.subscribe((value) => console.log(value));
promise vs observable
promise
.then((value) => console.log(value));
observable
.subscribe((value) => console.log(value));
Primitive value, array, promise, observable
number
Iterable
<number>
Promise
<number>
Observable
<number>
pull semantics
push semantics
Single Multiple
Now
In future
Observable - rxMarble
http://reactivex.io/assets/operators/legend.png
Debounce operator
Merge operator
Concat operator
Map operator
Filter operator
Age changes tariff vs Tariff is changed by age
Sum assuredPremium frequency MassHeigthAge
BMI
Loading
Tariff
Discount
Premium
Payment plan
map-filter-reduce --- recall
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
const double = (x) => 2 * x;
const isPrime = (n) => ...;
const add = (a, b) => a + b;
const doubled2 = array.map(double);
const primes2 = array.filter(isPrime);
const sum2 = array.reduce(add, 0);
Map operator
Filter operator
Reduce operator
Scan operator
REDUX pattern, ngrx
State…
• State – values of all variables that app has access to
…is projected on…
View…
• View is a function of state 𝑈𝐼 = 𝑓(𝑆𝑡𝑎𝑡𝑒)
…triggers…
Actions…
• Action – object describing what happened.
• Important – action emitting does not modify the state.
…sent to…
Reducer…
• Reducer – pure function, which takes old state and action and gives
the new state
• Important – reducer doesn’t modify the state – creates the new one.
• Important – reducer doesn’t do asynchronous tasks.
…updates…
Store…
• Store – single place which holds state of the application
• Important – State is read-only.
•  database
…contains…
State…
• State – values of all variables that app has access to
…is projected on…
REDUX pattern
Store
State
ViewActions
Reducer
Contains
Is projected on
Triggers
Sent to
Updated
https://medium.com/@flashMasterJim/setting-up-ngrx-store-in-an-angular-2-project-e5232a7b082e#.23yso3phj
Reducer interface
interface ActionReducer<T> {
(state: T, action: Action): T;
}
const dumbReducer = (state, action) => state;
Reducer from example application
export function reducer(state: State = defaultState, action: Action = {} as Action) {
switch (action.type) {
case LOAD_QUOTE_SUCCESS: {
return {...state, options: action.payload.options};
}
}
return state;
}
Reducer from example application
export function reducer(state: State = defaultState, action: Action = {} as Action) {
switch (action.type) {
case LOAD_QUOTE_SUCCESS: {
return {...state, options: action.payload.options};
}
}
return state;
}
Scan operator – recall
Architecture
@ngrx/Store
Service
Smart Comp.
Dumb Comp.
Reducers
new state
select state (observable)
state (observable)
properties (bind)events
call
action (dispatch)
state + action
https://youtu.be/pjwVq8B-ZAw @ 21:55
Architecture
@ngrx/Store
Service
Smart Comp.
Dumb Comp.
Reducers
@ngrx/Effects
Effects Service
Data Service
HTTP
new state
select state (observable)
state (observable)
properties (bind)events
call
action (dispatch)
state + action
action (dispatch)
state + action
state + action
state (call)
state (call)response (observable)
response (observable)
state + action
https://youtu.be/pjwVq8B-ZAw @ 21:55 & @ 33:45
@ngrx/StoreService
Smart Comp.
Dumb Comp.
Reducers
@ngrx/Effects
Effects Service
Data Service
HTTP
Service
Smart Comp.
Dumb Comp.
User action
event
call
dispatch select
state
bindings
@ngrx/StoreService
Smart Comp.
Dumb Comp.
Reducers
@ngrx/Effects
Effects Service
Data Service
HTTP
Service
Smart Comp.
Dumb Comp.
database
Inserts
& Updates
Queries
Triggers
Normalized state & derived values
Sum assuredPremium frequency MassHeigthAge
BMI
Loading
Tariff
Discount
Premium
Payment plan
Demo
Scalable architecture
1. Design API (both http and state)
2. Quick mock data service with static example data
3. Do in parallel:
• Put main logic in actions & reducers
• Put query logic in service
• Put side-effect logic in effects
• Create dumb component
• Create data service
4. Glue everything up
• Smart components bridges services and dumb components
• Dispatch events to store in services
5. Replace mock data service with real one (but you can still use mock in tests)
Scalable architecture
1. Design API (both http and state)
2. Quick mock data service with static example data
3. Do in parallel:
• Put main logic in actions & reducers
• Put query logic in service
• Put side-effect logic in effects
• Create dumb component
• Create data service
4. Glue everything up
• Smart components bridges services and dumb components
• Dispatch events to store in services
5. Replace mock data service with real one (but you can still use mock in tests)
One task can be in progress by 5 developers in one time*
Scalable architecture
1. Design API (both http and state)
2. Quick mock data service with static example data
3. Do in parallel:
• Put main logic in actions & reducers
• Put query logic in service
• Put side-effect logic in effects
• Create dumb component
• Create data service
4. Glue everything up
• Smart components bridges services and dumb components
• Dispatch events to store in services
5. Replace mock data service with real one (but you can still use mock in tests)
One task can be in progress by 5 developers in one time*
*even by 10 with pair programming ;)
Scalable applications
• Think in layers:
• What I want to insert into store
• What I want query from the store (and watch for changes in effective way!)
• What does communicate with other systems (not only backend)
• What I want to show
• Can I divide it into smaller parts
• Do I already have everything in the store
Additional benefits of REDUX pattern
• Transactional modification of the application state
• Seperation of View and Logic
• You can persist application state in any moment
• You can restore view from persisted application state
• Time-travel debug – bug reporting made easy
• Dirty checking – you have not to
• You can share some actions with backend
• Easy unit testing
Summary
Declarative, Observable, Redux, Architecture
map-filter-reduce
const array = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
const double = (x) => 2 * x;
const isPrime = (n) => ...;
const add = (a, b) => a + b;
const doubled2 = array.map(double);
const primes2 = array.filter(isPrime);
const sum2 = array.reduce(add, 0);
Observable - rxMarble
http://reactivex.io/assets/operators/legend.png
REDUX pattern
Store
State
ViewActions
Reducer
Contains
Is projected on
Triggers
Sent to
Updated
https://medium.com/@flashMasterJim/setting-up-ngrx-store-in-an-angular-2-project-e5232a7b082e#.23yso3phj
@ngrx/StoreService
Smart Comp.
Dumb Comp.
Reducers
@ngrx/Effects
Effects Service
Data Service
HTTP
Service
Smart Comp.
Dumb Comp.
User action
event
call
dispatch select
state
bindings
Q&A.

Architecture for scalable Angular applications

  • 1.
    Angular 2+ Architecture forscalable Angular applications Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
  • 2.
    Angular 2+ Architecture forscalable Angular applications Scalable architecture for Angular applications Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
  • 3.
    Agenda • @AboutMe() • Paradigms •Promise & array • Marbles • Pattern • Achitecture • Q&A Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
  • 4.
    Agenda • @AboutMe() • Paradigms •Promise & array • Marbles • Pattern • Achitecture • Q&A Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15 Mysterious, but get ready for a paradigm shift
  • 5.
    @AboutMe() • I startedprogramming 18 years ago • I code for money from the 9 years • I code for Decerto 3 years • I was programming in Pascal, C, PHP, Javascript, Bash, SQL, C++, Java, Typescript • I did some AJAX before it was popular • I did some UX before it was so popular • I did some Devops before it was so named • I was programming SPAs over the last 4 years in AngularJS • I observe the development of Angular 2 since AngularJS 1.5-beta.0 • I am doing SPA in Angular 2 & 4 the last few months Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
  • 6.
    @AboutMe() • I startedprogramming 18 years ago • I code for money from the 9 years • I code for Decerto 3 years • I was programming in Pascal, C, PHP, Javascript, Bash, SQL, C++, Java, Typescript • I did some AJAX before it was popular • I did some UX before it was so popular • I did some Devops before it was so named • I was programming SPAs over the last 4 years in AngularJS • I observe the development of Angular 2 since AngularJS 1.5-beta.0 • I am doing SPA in Angular 2 & 4 the last few months Paweł Żurowski <zurowski.pawel@gmail.com> 2017-09-15
  • 7.
  • 8.
    for vs forEach constarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; // for for (let i = 0; i < array.length; ++i) { console.log(array[i]); } // forEach array.forEach((item) => console.log(item));
  • 9.
    for vs map constarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; // for const doubled1 = []; for (let i = 0; i < array.length; ++i) { let double = array[i] * 2; doubled1.push(double); } // map const doubled2 = array.map((item) => 2 * item);
  • 10.
    for vs filter constarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; const isPrime = (n) => ...; // true or false // for const primes1 = []; for (let i = 0; i < array.length; ++i) { if(isPrime(array[i])){ primes1.push(array[i]); } } // filter const primes2 = array.filter(isPrime);
  • 11.
    for vs reduce constarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; // for let sum1 = 0; for (let i = 0; i < array.length; ++i) { sum1 = sum1 + array[i]; } // reduce const sum2 = array.reduce((last, item) => last + item, 0);
  • 12.
    for vs reduce constarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; const add = (a, b) => a + b; // for let sum1 = 0; for (let i = 0; i < array.length; ++i) { sum1 = add(sum1, array[i]); } // reduce const sum2 = array.reduce(add, 0);
  • 13.
    map-filter-reduce const array =[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; const double = (x) => 2 * x; const isPrime = (n) => ...; const add = (a, b) => a + b; const doubled2 = array.map(double); const primes2 = array.filter(isPrime); const sum2 = array.reduce(add, 0);
  • 14.
    map-filter-reduce const array =[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; const double = (x) => 2 * x; const isPrime = (n) => ...; const add = (a, b) => a + b; const doubled2 = array.map(double); const primes2 = array.filter(isPrime); const sum2 = array.reduce(add, 0);
  • 15.
    Primitive value, array,promise, … …
  • 16.
    Primitive value, array,promise, … number Array <number> Promise <number> ? Single Multiple Now In future
  • 17.
    Primitive value, array,promise, … number Array <number> Promise <number> ? Single Multiple Now In future
  • 18.
    Primitive value, array,promise, … number Array <number> Promise <number> ? Single Multiple Now In future
  • 19.
    Primitive value, array,promise, … number Array <number> Promise <number> Observable <number> Single Multiple Now In future
  • 20.
    promise vs observable promise .then((value) => console.log(value)) .catch( (reason) => console.error(reason)) .finally(() => console.log('Done')); observable.subscribe( (value) => console.log(value), (reason) => console.error(reason), () => console.log('Done') );
  • 21.
    promise vs observable promise .then((value) => console.log(value)) ; observable.subscribe( (value) => console.log(value) );
  • 22.
    promise vs observable promise .then((value)=> console.log(value)); observable .subscribe((value) => console.log(value));
  • 23.
    promise vs observable promise .then((value)=> console.log(value)); observable .subscribe((value) => console.log(value));
  • 24.
    Primitive value, array,promise, observable number Iterable <number> Promise <number> Observable <number> pull semantics push semantics Single Multiple Now In future
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
    Age changes tariffvs Tariff is changed by age Sum assuredPremium frequency MassHeigthAge BMI Loading Tariff Discount Premium Payment plan
  • 32.
    map-filter-reduce --- recall constarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; const double = (x) => 2 * x; const isPrime = (n) => ...; const add = (a, b) => a + b; const doubled2 = array.map(double); const primes2 = array.filter(isPrime); const sum2 = array.reduce(add, 0);
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
    State… • State –values of all variables that app has access to …is projected on…
  • 39.
    View… • View isa function of state 𝑈𝐼 = 𝑓(𝑆𝑡𝑎𝑡𝑒) …triggers…
  • 40.
    Actions… • Action –object describing what happened. • Important – action emitting does not modify the state. …sent to…
  • 41.
    Reducer… • Reducer –pure function, which takes old state and action and gives the new state • Important – reducer doesn’t modify the state – creates the new one. • Important – reducer doesn’t do asynchronous tasks. …updates…
  • 42.
    Store… • Store –single place which holds state of the application • Important – State is read-only. •  database …contains…
  • 43.
    State… • State –values of all variables that app has access to …is projected on…
  • 44.
    REDUX pattern Store State ViewActions Reducer Contains Is projectedon Triggers Sent to Updated https://medium.com/@flashMasterJim/setting-up-ngrx-store-in-an-angular-2-project-e5232a7b082e#.23yso3phj
  • 45.
    Reducer interface interface ActionReducer<T>{ (state: T, action: Action): T; } const dumbReducer = (state, action) => state;
  • 46.
    Reducer from exampleapplication export function reducer(state: State = defaultState, action: Action = {} as Action) { switch (action.type) { case LOAD_QUOTE_SUCCESS: { return {...state, options: action.payload.options}; } } return state; }
  • 47.
    Reducer from exampleapplication export function reducer(state: State = defaultState, action: Action = {} as Action) { switch (action.type) { case LOAD_QUOTE_SUCCESS: { return {...state, options: action.payload.options}; } } return state; }
  • 48.
  • 49.
    Architecture @ngrx/Store Service Smart Comp. Dumb Comp. Reducers newstate select state (observable) state (observable) properties (bind)events call action (dispatch) state + action https://youtu.be/pjwVq8B-ZAw @ 21:55
  • 50.
    Architecture @ngrx/Store Service Smart Comp. Dumb Comp. Reducers @ngrx/Effects EffectsService Data Service HTTP new state select state (observable) state (observable) properties (bind)events call action (dispatch) state + action action (dispatch) state + action state + action state (call) state (call)response (observable) response (observable) state + action https://youtu.be/pjwVq8B-ZAw @ 21:55 & @ 33:45
  • 51.
    @ngrx/StoreService Smart Comp. Dumb Comp. Reducers @ngrx/Effects EffectsService Data Service HTTP Service Smart Comp. Dumb Comp. User action event call dispatch select state bindings
  • 52.
    @ngrx/StoreService Smart Comp. Dumb Comp. Reducers @ngrx/Effects EffectsService Data Service HTTP Service Smart Comp. Dumb Comp. database Inserts & Updates Queries Triggers
  • 53.
    Normalized state &derived values Sum assuredPremium frequency MassHeigthAge BMI Loading Tariff Discount Premium Payment plan
  • 54.
  • 55.
    Scalable architecture 1. DesignAPI (both http and state) 2. Quick mock data service with static example data 3. Do in parallel: • Put main logic in actions & reducers • Put query logic in service • Put side-effect logic in effects • Create dumb component • Create data service 4. Glue everything up • Smart components bridges services and dumb components • Dispatch events to store in services 5. Replace mock data service with real one (but you can still use mock in tests)
  • 56.
    Scalable architecture 1. DesignAPI (both http and state) 2. Quick mock data service with static example data 3. Do in parallel: • Put main logic in actions & reducers • Put query logic in service • Put side-effect logic in effects • Create dumb component • Create data service 4. Glue everything up • Smart components bridges services and dumb components • Dispatch events to store in services 5. Replace mock data service with real one (but you can still use mock in tests) One task can be in progress by 5 developers in one time*
  • 57.
    Scalable architecture 1. DesignAPI (both http and state) 2. Quick mock data service with static example data 3. Do in parallel: • Put main logic in actions & reducers • Put query logic in service • Put side-effect logic in effects • Create dumb component • Create data service 4. Glue everything up • Smart components bridges services and dumb components • Dispatch events to store in services 5. Replace mock data service with real one (but you can still use mock in tests) One task can be in progress by 5 developers in one time* *even by 10 with pair programming ;)
  • 58.
    Scalable applications • Thinkin layers: • What I want to insert into store • What I want query from the store (and watch for changes in effective way!) • What does communicate with other systems (not only backend) • What I want to show • Can I divide it into smaller parts • Do I already have everything in the store
  • 59.
    Additional benefits ofREDUX pattern • Transactional modification of the application state • Seperation of View and Logic • You can persist application state in any moment • You can restore view from persisted application state • Time-travel debug – bug reporting made easy • Dirty checking – you have not to • You can share some actions with backend • Easy unit testing
  • 60.
  • 61.
    map-filter-reduce const array =[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; const double = (x) => 2 * x; const isPrime = (n) => ...; const add = (a, b) => a + b; const doubled2 = array.map(double); const primes2 = array.filter(isPrime); const sum2 = array.reduce(add, 0);
  • 62.
  • 63.
    REDUX pattern Store State ViewActions Reducer Contains Is projectedon Triggers Sent to Updated https://medium.com/@flashMasterJim/setting-up-ngrx-store-in-an-angular-2-project-e5232a7b082e#.23yso3phj
  • 64.
    @ngrx/StoreService Smart Comp. Dumb Comp. Reducers @ngrx/Effects EffectsService Data Service HTTP Service Smart Comp. Dumb Comp. User action event call dispatch select state bindings
  • 65.