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
170
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
92
Angular 17 - Rainaissance
gregonnet
0
280
Qwik for Angular Developers
gregonnet
0
170
Angular Material Test Harness
gregonnet
0
290
Qwik - Deliver instant apps at scale
gregonnet
0
410
Resilient UI Test Patterns
gregonnet
0
200
metaUI - Introduction
gregonnet
0
100
How to make tests more resilient
gregonnet
0
110
minsk-19-12-16
gregonnet
0
170
Other Decks in Programming
See All in Programming
AI 開発合宿を通して得た学び
niftycorp
PRO
0
150
コードレビューをしない選択 #でぃーぷらすトウキョウ
kajitack
3
1k
20260228_JAWS_Beginner_Kansai
takuyay0ne
5
590
AI駆動開発の本音 〜Claude Code並列開発で見えたエンジニアの新しい役割〜
hisuzuya
4
520
Windows on Ryzen and I
seosoft
0
320
クライアントワークでSREをするということ。あるいは事業会社におけるSREと同じこと・違うこと
nnaka2992
1
350
Codex の「自走力」を高める
yorifuji
0
1.2k
20260313 - Grafana & Friends Taipei #1 - Kubernetes v1.36 的開發雜記:那些困在 Alpha 加護病房太久的 Metrics
tico88612
0
220
野球解説AI Agentを開発してみた - 2026/02/27 LayerX社内LT会資料
shinyorke
PRO
0
350
Claude Codeログ基盤の構築
giginet
PRO
7
3.4k
Ruby and LLM Ecosystem 2nd
koic
1
1k
エラーログのマスキングの仕組みづくりに役立ったASTの話
kumoichi
0
250
Featured
See All Featured
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
320
Documentation Writing (for coders)
carmenintech
77
5.3k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
67
37k
Product Roadmaps are Hard
iamctodd
PRO
55
12k
Discover your Explorer Soul
emna__ayadi
2
1.1k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
Practical Orchestrator
shlominoach
191
11k
Testing 201, or: Great Expectations
jmmastey
46
8.1k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
62
52k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
240
Art, The Web, and Tiny UX
lynnandtonic
304
21k
How to build a perfect <img>
jonoalderson
1
5.3k
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