Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Angular at Scale | Web Developer Chemnitz
Search
Gregor Woiwode
November 28, 2019
Programming
0
160
Angular at Scale | Web Developer Chemnitz
Gregor Woiwode
November 28, 2019
Tweet
Share
More Decks by Gregor Woiwode
See All by Gregor Woiwode
Pride and Prejudice - Template Driven Forms
gregonnet
0
87
Angular 17 - Rainaissance
gregonnet
0
280
Qwik for Angular Developers
gregonnet
0
160
Angular Material Test Harness
gregonnet
0
280
Qwik - Deliver instant apps at scale
gregonnet
0
390
Resilient UI Test Patterns
gregonnet
0
200
metaUI - Introduction
gregonnet
0
94
How to make tests more resilient
gregonnet
0
110
minsk-19-12-16
gregonnet
0
160
Other Decks in Programming
See All in Programming
はじめてのカスタムエージェント【GitHub Copilot Agent Mode編】
satoshi256kbyte
0
140
PC-6001でPSG曲を鳴らすまでを全部NetBSD上の Makefile に押し込んでみた / osc2025hiroshima
tsutsui
0
200
例外処理とどう使い分ける?Result型を使ったエラー設計 #burikaigi
kajitack
15
3.8k
Cell-Based Architecture
larchanjo
0
160
メルカリのリーダビリティチームが取り組む、AI時代のスケーラブルな品質文化
cloverrose
2
450
perlをWebAssembly上で動かすと何が嬉しいの??? / Where does Perl-on-Wasm actually make sense?
mackee
0
290
副作用をどこに置くか問題:オブジェクト指向で整理する設計判断ツリー
koxya
1
220
ゆくKotlin くるRust
exoego
1
190
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
syumai
7
2.4k
生成AIを利用するだけでなく、投資できる組織へ
pospome
2
440
Denoのセキュリティに関する仕組みの紹介 (toranoana.deno #23)
uki00a
0
210
愛される翻訳の秘訣
kishikawakatsumi
3
370
Featured
See All Featured
Odyssey Design
rkendrick25
PRO
0
450
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.5k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
270
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.6k
How GitHub (no longer) Works
holman
316
140k
How to make the Groovebox
asonas
2
1.9k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
360
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
0
400
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
220
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.8k
Test your architecture with Archunit
thirion
1
2.1k
Everyday Curiosity
cassininazir
0
120
Transcript
Angular at Scale …with Redux powered by NgRx
Gregor Woiwode
Gregor Woiwode co-IT.eu GmbH @GregOnNet
Kentan NgRx Ducks Schematics
@GregOnNet Topics Common Pitfalls Why, How & What of State
Management Redux Architecture NgRx platform
function iDoToMuch(input: any) { if (input.hasPropertyA) { !/* !!... !*/
if (input.hasAlsoPropertyA2) { !/* !!... !*/ } else if (input.hasAlsoPropertyA3) { input.propertyList.map(property !=> { !/* !!... !*/ }); } } if (input.hasPropertyB) { !/* !!... !*/ } input = updateInput(input); return input.fullFillsCondition ? changeInputTheOneWay(input) : changeInputTheOtherWay(input); } Nested Code Blocks Cyclometic Complexity of 10 Tests??? Complexity @GregOnNet
@GregOnNet
Modal Wrapper Monoliths Modal Stepper Questionnaire Button Group @GregOnNet
Modal Wrapper Monoliths Modal Stepper Questionnaire Button Group No clear
API Deeply nested Components Hard for others to understand Hard to maintain @GregOnNet
Monoliths Reduce Component Nesting Think in APIs Put a smile
on your colleagues’ face @GregOnNet
Learnings Applying valuable patterns is hard. It is nearly impossible
to know the whole Project you are working on. It is often ignored that requirements change over time. @GregOnNet
Decentralised state @GregOnNet State is spread across multiple components.
@GregOnNet Distant leaf components need access to state. Decentralised state
Centralised state Interact with single source @GregOnNet
Store Redux Component Action Reducers @GregOnNet Dispatch Processed Mutate Read
Redux Component Action Reducers @GregOnNet Read Write
Redux implements Command-Query-Segregation. @GregOnNet
Redux separates read- and write logic. @GregOnNet
Redux allows writing smaller modules. @GregOnNet
Redux decentralises your code. @GregOnNet
@GregOnNet interface Action { type: string } Action
@GregOnNet interface Action { type: string } Action type needs
to be unique Represents an interaction with UI or Data Takes an optional payload or metadata
@GregOnNet Action interface Action<T> { type: string payload: T }
Reducer Reducer A Reducer B Reduce @GregOnNet A reducer is
responsible for processing actions
Reducer Reducer A Reducer B Reduce @GregOnNet
Reducer Reducer B @GregOnNet Function
Store Principles @GregOnNet Read-Only Single Source of Truth Mutations are
made by pure functions
Pure function @GregOnNet function iAmPure(input: string): string { return `${input}1`;
}
Impure function @GregOnNet function iAmImpurePure(): string { const input =
document.querySelector('input'); return `${input.value}1`; }
Store-Slices @GregOnNet Reducer B Reducer A Reducer C
Store @GregOnNet Reducer B Reducer A Reducer C const state
= { featureA: { !/* !!... !*/ }, featureB: { !/* !!... !*/ }, featureC: { !/* !!... !*/ } } Store is an In-Memory data structure
@GregOnNet NgRx https://ngrx.io/
@GregOnNet @NgModule({ imports: [ StoreModule.forRoot(reducers, { }) !/* !!... !*/
}) export class AppModule { } StoreModule
@GregOnNet @NgModule({ imports: [ StoreModule.forRoot(reducers, { runtimeChecks: { } })
!/* !!... !*/ }) export class AppModule { } StoreModule
@GregOnNet StoreModule @NgModule({ imports: [ StoreModule.forRoot(reducers, { runtimeChecks: { strictActionImmutability:
true, strictActionSerializability: true, strictStateImmutability: true, strictStateSerializability: true } }) !/* !!... !*/ }) export class AppModule { }
@GregOnNet StoreModule - reducers export const reducers: ActionReducerMap<State> = {
counter: counterReducer, logger: loggerReducer } Reducers get combined feature name
@GregOnNet createAction export const increment = createAction( '[Counter] Increment by
one' ); !// createAction(); !// !// { !// type: '[Counter] Increment by one' !// }
@GregOnNet createAction & payload export const add = createAction( '[Counter]
Add value', props<{ payload: { value: number } }>() ); !// createAction({ payload: { value: 1 } }) !// !// { !// type:'[Counter] Add value', !// payload: { value: 1} !// }
@GregOnNet Read from the store export class SimpleCounterComponent { count$:
Observable<number>; constructor(private store: Store<State>) { this.count$ = this.store.pipe( select(state !=> state.counter.count) ); } }
@GregOnNet DEMO
Redux - Inter app communication Component Action Reducers @GregOnNet Dispatch
Processed Mutate Read
Redux - Side effects Component Action Reducers @GregOnNet Dispatch Action
Effects Dispatch
@GregOnNet @NgModule({ imports: [ !/* !!... !*/, EffectsModule.forRoot([CounterEffects]) }) export
class AppModule { } EffectsModule
Redux - Side effects @GregOnNet @Injectable() export class CounterEffects {
randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd) )); constructor(private actions: Actions) { } }
Redux - Side effects @GregOnNet @Injectable() export class CounterEffects {
randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* e.g. call API !*/) )); constructor(private actions: Actions) { } }
Redux - Side effects @GregOnNet @Injectable() export class CounterEffects {
randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* e.g. call API !*/), map((result !=> randomAddSuccess(result)) )); constructor(private actions: Actions) { } }
Redux - Side effects @GregOnNet @Injectable() export class CounterEffects {
randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* e.g. call API !*/), ), { dispatch: true|false }); constructor(private actions: Actions) { } }
Redux - Side effects @GregOnNet @Injectable() export class CounterEffects {
randomAdd = createEffect(() !=> this.actions.pipe( ofType(randomAdd), switchMap((action !=> !/* call API !*/), map((result !=> randomAddSuccess(result)) ), { resubscribeOnError: true|false }); constructor(private actions: Actions) { } }
@GregOnNet DEMO
Selectors @GregOnNet this.store.pipe( select(state !=> state.counter.count) ); Inline selectors
Selectors @GregOnNet this.store.pipe( select(state !=> state.cou ); Break quickly when
Store changes Not reusable
Selectors @GregOnNet const featureCounter = createFeatureSelector<CounterState>('counter'); feature name Jump to
a feature
Selectors @GregOnNet export const count = createSelector(featureCounter, s !=> s.count);
Project data const featureCounter = createFeatureSelector<CounterState>('counter');
Selectors @GregOnNet Combine selectors aggregating data export const infoWithCurrentCount =
createSelector( infoMessage, count, (messages, total) !=> messages.map(message !=> `${message} (Total: ${total})` ) );
@GregOnNet Selectors simplify Store changes.
@GregOnNet are composable. Selectors
@GregOnNet use memoization. Selectors
@GregOnNet Selectors Memoization 1 selector calculate 2 selector use cache
3 selector calculate
@GregOnNet DEMO
What we have covered… Redux Architecture Actions & Reducers StoreDevtools
Effects Selectors @ngrx/entity @ngrx/router-store @ngrx/data
Closing thoughts On-Board your team patiently. Modeling application state has
lots of overlaps with Requirements Engineering. Redux makes your App more predictive.
Gregor Woiwode Thank you for having me! @GregOnNet https://bit.do/ng-scale-chemnitz https://stackblitz.com/edit/ngrx-8-playground