Redux, RxJS oraz
koncept AppSandbox'a czyli
Angular na sterydach
Rafał Brzoska, Łukasz Max Kokoszka
Frontend Wolves
Rafał Brzoska
- Senior Software Developer
- JS, TS, Angular
- Organizator Angular Silesia
- Angular Trainer, Prelegent
- Bottega IT Mind
Łukasz Max Kokoszka
- Senior Software Developer
- JS & Frontend
- Internal Trainer, Prelegent
Redux, RxJS oraz
koncept AppSandbox'a
czyli
Angular na sterydach
Z czego wzięła się cała nasza rozkmina?
Jeden produkt o spójnym UX
Różnorodność wymagań
Rozproszony development
Wymagania architektoniczne
Szkielet (Core)
Globalizacja (g11n)
Bezpieczeństwo
UX
Pozostałe elementy wspólne
Feature #1
Uwierzytelnienie, Autoryzacja, Dane
Lokalizacja, Internacjonalizacja
Wspólne komponenty i kontenery
Routing, Serwisy, Storage
Moduły biznesowe
Feature #2
Fantastyczna czwórka
+
+
=
Angular
TypeScript
Angular CLI
Components
Routing (Guards, Lazy Loading)
Angular Module (NgModule)
RxJS
Redux
NgRx
NgRx
Biblioteka Angular
@store, @effects, @router-store, @store-devtools, @schematics
ObservableStore
Silnie typowane Akcje
NgRx
subscription
Store
Effect
View
Action
Reducer
Action
Component
Dodatkowe wyzwania
Kontrola developmentu
Kontrola stanu
Deployment i budowanie
S jak Sandbox
Core
Store
Sandbox
Feature #1
Feature #3
Feature #2
Services
Shared components
Services
Services
Services
Components
Components
Components
Store
Store
Store
Sandbox
Sandbox
Sandbox
app.module.ts
app.sandbox.ts
app
store
core
features
feature1.module.ts
feature1.sandbox.ts
store
feature1
feature2.module.ts
feature2.sandbox.ts
store
feature2
actions
reducers
effects
selectors
states
Sandbox
Budowa Sandboxa
@Injectable()
class AppSandbox {
constructor(
private appState$: Store<AppState>,
private actionsSubj: ActionsSubject,
private childSandbox: ChildSandbox) {}
dataStream$ = this.appState$.select(dataSelector);
}
Components
Store
Data streams
Dispatchers
Special (observable) dispatchers
@Injectable()
class AppSandbox {
constructor(
private appState$: Store<AppState>,
private actionsSubj: ActionsSubject,
private childSandbox: ChildSandbox) {}
dataStream$ = this.appState$.select(dataSelector);
doSomething(newData) {
this.appState$.dispatch(new DoSomethingAction(newData))
}
}
@Injectable()
class AppSandbox {
constructor(
private appState$: Store<AppState>,
private actionsSubj: ActionsSubject,
private childSandbox: ChildSandbox) {}
dataStream$ = this.appState$.select(dataSelector);
doSomething(newData) {
this.appState$.dispatch(new DoSomethingAction(newData))
}
doSomethingSpecial$$(): Observable<string> {
return getDispatchObservable(
this.actionsSubject,
this.appState$,
SampleActionTypes.DoSomethingSpecialSuccess,
SampleActionTypes.DoSomethingSpecialFailure,
new DoSomethingSpecialAction()
);
}
}
@Injectable()
class AppSandbox {
constructor(
private appState$: Store<AppState>,
private actionsSubj: ActionsSubject,
private childSandbox: ChildSandbox) {}
}
Services
Services
@Injectable()
class AppSandbox {
constructor(
private appState$: Store<AppState>,
private actionsSubj: ActionsSubject,
private childSandbox: ChildSandbox) {}
dataStream$ = this.appState$.select(dataSelector);
doSomething(newData) {
this.appState$.dispatch(new DoSomethingAction(newData))
}
doSomethingSpecial$$(): Observable<string> {
return getDispatchObservable(
this.actionsSubject,
this.appState$,
SampleActionTypes.DoSomethingSpecialSuccess,
SampleActionTypes.DoSomethingSpecialFailure,
new DoSomethingSpecialAction()
);
}
doSomethingElse(data) {
this.childSandbox.doSomethingElse(data);
}
}
Re-exports
// src/app/component/sample.component.ts
// ...
@Component({
selector: 'sample',
templateUrl: './sample.component.html', styleUrls: ['./sample.component.scss']
})
export class SampleComponent implement OnInit, OnDestroy {
dataStream$: Observable<any>;
constructor(private appSandbox: AppSandbox, private childSandbox: ChildSandbox) {
this.dataStream$ = this.appSandbox.dataStream$;
}
}
// src/app/component/sample.component.ts
// ...
@Component({
selector: 'sample',
templateUrl: './sample.component.html', styleUrls: ['./sample.component.scss']
})
export class SampleComponent implement OnInit, OnDestroy {
dataStream$: Observable<any>;
constructor(private appSandbox: AppSandbox, private childSandbox: ChildSandbox) {
this.dataStream$ = this.appSandbox.dataStream$;
}
ngOnInit() {
this.routerSubscription = this.appSandbox.routeParams$.pipe(take(1))
.subscribe((p: Params) => {}, () => {});
}
}
// src/app/component/sample.component.ts
// ...
@Component({
selector: 'sample',
templateUrl: './sample.component.html', styleUrls: ['./sample.component.scss']
})
export class SampleComponent implement OnInit, OnDestroy {
dataStream$: Observable<any>;
constructor(private appSandbox: AppSandbox, private childSandbox: ChildSandbox) {
this.dataStream$ = this.appSandbox.dataStream$;
}
ngOnInit() {
this.routerSubscription = this.appSandbox.routeParams$.pipe(take(1))
.subscribe((p: Params) => this.appSandbox.doSomething(p.param1), () => {});
}
buttonClicked() {
this.childSandbox.doSomethingElse();
}
ngOnDestroy() {
this.routerSubscription.unsubscribe();
}
}
// src/app/component/sample.component.ts
// ...
@Component({
selector: 'sample',
templateUrl: './sample.component.html', styleUrls: ['./sample.component.scss']
})
export class SampleComponent implement OnInit, OnDestroy {
dataStream$: Observable<any>;
constructor(private appSandbox: AppSandbox, private childSandbox: ChildSandbox) {
this.dataStream$ = this.appSandbox.dataStream$;
}
ngOnInit() {
this.routerSubscription = this.appSandbox.routeParams$.pipe(take(1))
.subscribe((p: Params) => this.appSandbox.doSomething(p.param1), () => {});
}
}
// src/app/component/sample.component.ts
// ...
@Component({
selector: 'sample',
templateUrl: './sample.component.html', styleUrls: ['./sample.component.scss']
})
export class SampleComponent implement OnInit, OnDestroy {
dataStream$: Observable<any>;
constructor(private appSandbox: AppSandbox, private childSandbox: ChildSandbox) {
this.dataStream$ = this.appSandbox.dataStream$;
}
ngOnInit() {
this.routerSubscription = this.appSandbox.routeParams$.pipe(take(1))
.subscribe((p: Params) => this.appSandbox.doSomething(p.param1), () => {});
}
buttonClicked() {
this.childSandbox.doSomethingElse();
}
}
Gratisy
Mało zależności w komponentach
Agregacja zależności
Uproszczone testowanie
Problemy
Duży przyrost kodu NgRx
Ograniczone wsparcie
ze strony narzędzi
Puchnący Sandbox
Ciężkie wejście dla nowych osób w projekcie
Pułapki RxJS i NgRx
Go & code! :-)
linkedin.com/in/lukasz-max-kokoszka
twitter.com/rafal_brzoska
linkedin.com/in/rafalbrzoska
Dziękujemy! :-)
linkedin.com/in/lukasz-max-kokoszka
twitter.com/rafal_brzoska
linkedin.com/in/rafalbrzoska
FDD2018 - Angular App Sandbox
By Rafał Brzoska
FDD2018 - Angular App Sandbox
- 111