Pro Yearly is on sale from $80 to $50! »

Quantum Facades: Why NgRx Facades are terrible or awesome depending on how you observe them (AngularConnect 2019)

7beed3a6fa39e12c9e873b903e4d9244?s=47 Sam Julien
September 20, 2019

Quantum Facades: Why NgRx Facades are terrible or awesome depending on how you observe them (AngularConnect 2019)

NgRx facades are terrible! No wait, they're the best thing ever! What the heck is a facade, anyway? You may have heard recently about a topic whizzing around the Angular community: facades in NgRx. But are facades good or bad? Much like the age-old controversy of whether light is a particle or a wave, the answer depends on how you look at them. In this talk, you'll learn what facades are, why you should embrace them, why you should never, ever use them, and how to implement one without making Mike Ryan wake up in a cold sweat somewhere in Alabama.

7beed3a6fa39e12c9e873b903e4d9244?s=128

Sam Julien

September 20, 2019
Tweet

Transcript

  1. Quantum Facades Why NgRx Facades are Great or Terrible Depending

    on How You Look at Them
  2. @samjulien Facades are Controversial

  3. @samjulien

  4. @samjulien

  5. @samjulien What the heck is a facade?

  6. Sam Julien @samjulien @samjulien

  7. Sam Julien @samjulien Developer Advocate Engineer at Auth0 @samjulien

  8. Sam Julien @samjulien Developer Advocate Engineer at Auth0 GDE &

    Angular Collaborator @samjulien
  9. Sam Julien @samjulien Developer Advocate Engineer at Auth0 GDE &

    Angular Collaborator UpgradingAngularJS.com @samjulien
  10. Sam Julien @samjulien Developer Advocate Engineer at Auth0 GDE &

    Angular Collaborator UpgradingAngularJS.com @samjulien Author at Thinkster.io
  11. @samjulien

  12. @samjulien @samjulien

  13. @samjulien

  14. @samjulien What the heck is a facade?

  15. @samjulien @samjulien

  16. @samjulien

  17. @samjulien

  18. @samjulien

  19. @samjulien @samjulien

  20. @samjulien @samjulien

  21. @samjulien Facades in Code

  22. @samjulien The Facade Pattern

  23. @samjulien The Facade Pattern

  24. @samjulien Client Service A Service B Service C

  25. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  26. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  27. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  28. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  29. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  30. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  31. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  32. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }⠀

    }⠀
  33. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }⠀

    }⠀
  34. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }⠀

    }⠀ class OtherClient { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }⠀ }⠀
  35. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }⠀

    }⠀ class OtherClient { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }⠀ }⠀
  36. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); }

    } class OtherClient { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); } }
  37. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); }

    } class OtherClient { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); } }
  38. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); }⠀

    }⠀ class OtherClient { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); }⠀ }⠀
  39. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); }⠀

    }⠀ class OtherClient { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); }⠀ }⠀ class OtherOtherClient { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceE.update(); }⠀ }⠀
  40. @samjulien

  41. @samjulien

  42. @samjulien Client Service A Service B Service C

  43. @samjulien Facade Client Service A Service B Service C

  44. @samjulien class Client { doSomething() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  45. @samjulien class Facade { getEverything() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  46. @samjulien class Facade { getEverything() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  47. @samjulien class Client { doSomething() { this.facade.getEverything(); } }

  48. @samjulien class Client { doSomething() { this.facade.getEverything(); } }

  49. @samjulien class Facade { getEverything() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  50. @samjulien class Facade { getEverything() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceC.getStuff(); }

    }
  51. @samjulien class Facade { getEverything() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }

    }
  52. @samjulien class Facade { getEverything() { this.serviceA.getStuff(); this.serviceB.getStuff(); this.serviceD.getMore(); }

    }
  53. @samjulien class Client { doSomething() { this.facade.getEverything(); }⠀ }⠀

  54. @samjulien class Client { doSomething() { this.facade.getEverything(); }⠀ }⠀ class

    OtherClient { doSomething() { this.facade.getEverything(); }⠀ }⠀
  55. @samjulien Facades in NgRx

  56. @samjulien

  57. @samjulien @samjulien

  58. @samjulien @samjulien

  59. @samjulien

  60. @samjulien

  61. Reducers Components Effects NgRx @samjulien

  62. Reducers Components Effects Changing State: Actions @samjulien

  63. @samjulien

  64. @samjulien [Magnets] Cool Magnets [Beam One] Start Beam One [Collider]

    Find Higgs Boson?? [Beam Two] Start Beam Two
  65. @samjulien [Magnets] Cool Magnets [Beam One] Start Beam One [Collider]

    Find Higgs Boson?? [Beam Two] Start Beam Two
  66. @samjulien export const startBeamOne = createAction( '[Beam One] Start Beam

    One' );
  67. Reducers Components Effects Reading State: Selectors @samjulien

  68. @samjulien

  69. @samjulien Magnet Temperature Beam One Status Collider Status Beam Two

    Status
  70. @samjulien store.select(selectBeamOneStatus);

  71. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  72. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  73. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  74. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  75. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  76. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  77. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  78. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } } ☹
  79. @samjulien Facade Client Service A Service B Service C

  80. Reducers Components Effects NgRx @samjulien

  81. Reducers Components Effects NgRx + Facades Facades @samjulien

  82. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  83. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  84. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  85. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  86. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  87. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  88. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private facade:

    BeamOneFacade) { this.beamOneStatus$ = this.facade.beamOneStatus$; } turnOn() { this.facade.turnOn(); } }
  89. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private facade:

    BeamOneFacade) { this.beamOneStatus$ = this.facade.beamOneStatus$; } turnOn() { this.facade.turnOn(); } }
  90. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private facade:

    BeamOneFacade) { this.beamOneStatus$ = this.facade.beamOneStatus$; } turnOn() { this.facade.turnOn(); } }
  91. @samjulien @Component({...}) export class BeamOneComponent { beamOneStatus$: Observable<BeamStatus>; constructor(private facade:

    BeamOneFacade) { this.beamOneStatus$ = this.facade.beamOneStatus$; } turnOn() { this.facade.turnOn(); } }
  92. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  93. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOneAndMagnet); } }
  94. @samjulien

  95. @samjulien

  96. @samjulien Facades are awesome!

  97. @samjulien Component doesn’t need to know about the store!

  98. @samjulien But, wait a just a sec…

  99. @samjulien class Client { doSomething() { this.facade.getEverything(); }⠀ }⠀

  100. @samjulien class Client { doSomething() { this.facade.getEverything(); }⠀ }⠀ class

    OtherClient { doSomething() { this.facade.getEverything(); }⠀ }⠀
  101. @samjulien

  102. @samjulien [Magnets] Cool Magnets [Beam One] Start Beam One [Collider]

    Find Higgs Boson?? [Beam Two] Start Beam Two
  103. @samjulien [Magnets] Cool Magnets [Beam One] Start Beam One [Collider]

    Find Higgs Boson?? [Beam Two] Start Beam Two
  104. @samjulien [Magnets] Cool Magnets [Beam One] Start Beam One [Collider]

    Find Higgs Boson?? [Beam Two] Start Beam Two
  105. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamTwoStatus$; } }
  106. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamTwoStatus$; } }
  107. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamTwoStatus$; } }
  108. @samjulien “Can you make one change? It’ll be really easy.”

    Your Boss (Probably)
  109. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamTwoStatus$; }⠀ }⠀
  110. @samjulien private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade this.beamTwoStatus$

    = this.beamTwoFacade }⠀ }⠀
  111. @samjulien private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade this.beamTwoStatus$

    = this.beamTwoFacade }⠀ turnOnBeams() { this.beamOneFacade.turnOn(); this.beamTwoFacade.turnOn(); }⠀ }⠀
  112. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamTwoStatus$; }⠀ turnOnBeams() { this.beamOneFacade.turnOn(); this.beamTwoFacade.turnOn(); }⠀ }⠀
  113. @samjulien We’re reusing actions!

  114. @samjulien [Beam One] Start Beam One [Beam Two] Start Beam

    Two
  115. @samjulien

  116. @samjulien

  117. @samjulien Did these come from the beam components or the

    collider component?
  118. @samjulien turnOnBeams() { this.beamOneFacade.turnOn(); this.beamTwoFacade.turnOn(); }

  119. @samjulien We’ve returned to services and abandoned NgRx architecture!

  120. @samjulien turnOnBeams() { this.beamOneFacade.turnOn(); this.beamTwoFacade.turnOn(); }

  121. @samjulien turnOnBeams() { this.store.dispatch(ColliderActionTypes.startBeams); }

  122. @samjulien createEffect(() => { return this.actions$.pipe( ofType(ColliderActionTypes.startBeams), exhaustMap(() => {

    // Call beam service }) ); });
  123. @samjulien createEffect(() => { return this.actions$.pipe( ofType(ColliderActionTypes.startBeams), exhaustMap(() => {

    // Call beam service }) ); });
  124. @samjulien createEffect(() => { return this.actions$.pipe( ofType(ColliderActionTypes.startBeams), exhaustMap(() => {

    // Call beam service }) ); });
  125. @samjulien createReducer( initialState, on(ColliderActionTypes.startBeams, state => { return { ...state,

    beam1: true, beam2: true }; }) );
  126. @samjulien createReducer( initialState, on(ColliderActionTypes.startBeams, state => { return { ...state,

    beam1: true, beam2: true }; }) );
  127. @samjulien createReducer( initialState, on(ColliderActionTypes.startBeams, state => { return { ...state,

    beam1: true, beam2: true }; }) );
  128. @samjulien [Beam One] Start Beam One [Beam Two] Start Beam

    Two
  129. @samjulien [Collider] Start Both Beams

  130. @samjulien Let Effects & Reducers Manage State

  131. @samjulien How do we fix this?

  132. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  133. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } turnOn() { this.store.dispatch(BeamOneActionTypes.startBeamOne); } }
  134. @samjulien @Injectable({...}) export class BeamOneFacade { beamOneStatus$: Observable<BeamStatus>; constructor(private store:

    Store<State>) { this.beamOneStatus$ = this.store.select(selectBeamOneStatus); } dispatch(action: Action) { this.store.dispatch(action); } }
  135. @samjulien

  136. @samjulien

  137. @samjulien turnOnBeams() { this.beamOneFacade.turnOn(); this.beamTwoFacade.turnOn(); }

  138. @samjulien turnOnBeams() { this.beamOneFacade.turnOn(); this.beamTwoFacade.turnOn(); }

  139. @samjulien What about those selectors?

  140. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamOneStatus$; } }
  141. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamOneStatus$; } }
  142. @samjulien “Can you make one more change? It’ll be SUPER

    easy.” Your Boss (Again)
  143. @samjulien @Component({...}) export class ColliderComponent { beamOneStatus$: Observable<BeamStatus>; beamTwoStatus$: Observable<BeamStatus>;

    constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) { this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamOneStatus$; }⠀ }⠀
  144. @samjulien constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) {

    this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamOneStatus$; }⠀ ⠀
  145. @samjulien constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) {

    this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamOneStatus$; this.bothBeams$ = combineLatest( this.beamOneStatus$, this.beamTwoStatus$ ).pipe( map(([beamOne, beamTwo]) => beamOne && beamTwo) ); }
  146. @samjulien constructor( private beamOneFacade: BeamOneFacade, private beamTwoFacade: BeamTwoFacade ) {

    this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$; this.beamTwoStatus$ = this.beamTwoFacade.beamOneStatus$; this.bothBeams$ = combineLatest( this.beamOneStatus$, this.beamTwoStatus$ ).pipe( map(([beamOne, beamTwo]) => beamOne && beamTwo) ); }
  147. @samjulien We’ve introduced a performance

  148. @samjulien combineLatest( this.beamOneStatus$, this.beamTwoStatus$ ).pipe( map(([beamOne, beamTwo]) => { return

    beamOne && beamTwo; }) );
  149. @samjulien combineLatest()

  150. @samjulien combineLatest() a

  151. @samjulien combineLatest() a 1 a1

  152. @samjulien combineLatest() a 1 a1 b b1

  153. @samjulien combineLatest() a 1 a1 b b1 2 b2

  154. @samjulien combineLatest( this.beamOneStatus$, this.beamTwoStatus$ ).pipe( map(([beamOne, beamTwo]) => { return

    beamOne && beamTwo; }) );
  155. @samjulien How do we fix this?

  156. @samjulien createSelector( selectBeamOneStatus, selectBeamTwoStatus, (beamOne, beamTwo) => { return beamOne

    && beamTwo; } );
  157. @samjulien createSelector( selectBeamOneStatus, selectBeamTwoStatus, (beamOne, beamTwo) => { return beamOne

    && beamTwo; } );
  158. @samjulien createSelector( selectBeamOneStatus, selectBeamTwoStatus, (beamOne, beamTwo) => { return beamOne

    && beamTwo; } );
  159. @samjulien turnOnBeams() { this.beamOneFacade.turnOn(); this.beamTwoFacade.turnOn(); }

  160. @samjulien @Injectable({...}) export class ColliderFacade { combinedStatus$: Observable<CombinedStatus>; constructor(private store:

    Store<State>) { this.combinedStatus$ = this.store.select(selectCombinedStatus); }⠀ dispatch(action: Action) { this.store.dispatch(action); }⠀ }⠀
  161. @samjulien @Injectable({...}) export class ColliderFacade { combinedStatus$: Observable<CombinedStatus>; constructor(private store:

    Store<State>) { this.combinedStatus$ = this.store.select(selectCombinedStatus); }⠀ dispatch(action: Action) { this.store.dispatch(action); }⠀ }⠀
  162. @samjulien @Injectable({...}) export class ColliderFacade { combinedStatus$: Observable<CombinedStatus>; constructor(private store:

    Store<State>) { this.combinedStatus$ = this.store.select(selectCombinedStatus); }⠀ dispatch(action: Action) { this.store.dispatch(action); }⠀ }⠀
  163. @samjulien @Injectable({...}) export class ColliderFacade { combinedStatus$: Observable<CombinedStatus>; constructor(private store:

    Store<State>) { this.combinedStatus$ = this.store.select(selectCombinedStatus); }⠀ dispatch(action: Action) { this.store.dispatch(action); }⠀ }⠀
  164. @samjulien @Injectable({...}) export class ColliderFacade { combinedStatus$: Observable<CombinedStatus>; magnetStatus$: Observable<MagnetStatus>;

    constructor(private store: Store<State>) { this.combinedStatus$ = this.store.select(selectCombinedStatus); this.magnetStatus$ = this.store.select(selectMagnetStatus); }⠀ dispatch(action: Action) { this.store.dispatch(action); }⠀ }⠀
  165. @samjulien @Injectable({...}) export class ColliderFacade { combinedStatus$: Observable<CombinedStatus>; constructor(private store:

    Store<State>) { this.combinedStatus$ = this.store.select(selectCombinedStatus); }⠀ dispatch(action: Action) { this.store.dispatch(action); }⠀ }⠀
  166. @samjulien @Injectable({...}) export class ColliderFacade { combinedStatus$: Observable<CombinedStatus>; constructor(private store:

    Store<State>) { this.combinedStatus$ = this.store.select(selectCombinedStatus); }⠀ dispatch(action: Action) { this.store.dispatch(action); }⠀ }⠀
  167. @samjulien @Component({...}) export class ColliderComponent { combinedStatus$: Observable<CombinedStatus>; constructor(private colliderFacade:

    ColliderFacade) { this.combinedStatus$ = this.colliderFacade.combinedStatus$; } turnOnBeams() { this.colliderFacade.dispatch(ColliderActionTypes.turnBeamsOn); } }
  168. @samjulien @Component({...}) export class ColliderComponent { combinedStatus$: Observable<CombinedStatus>; constructor(private colliderFacade:

    ColliderFacade) { this.combinedStatus$ = this.colliderFacade.combinedStatus$; } turnOnBeams() { this.colliderFacade.dispatch(ColliderActionTypes.turnBeamsOn); } }
  169. @samjulien @Component({...}) export class ColliderComponent { combinedStatus$: Observable<CombinedStatus>; constructor(private colliderFacade:

    ColliderFacade) { this.combinedStatus$ = this.colliderFacade.combinedStatus$; } turnOnBeams() { this.colliderFacade.dispatch(ColliderActionTypes.turnBeamsOn); } }
  170. @samjulien @Component({...}) export class ColliderComponent { combinedStatus$: Observable<CombinedStatus>; constructor(private colliderFacade:

    ColliderFacade) { this.combinedStatus$ = this.colliderFacade.combinedStatus$; } turnOnBeams() { this.colliderFacade.dispatch(ColliderActionTypes.turnBeamsOn); } }
  171. @samjulien

  172. @samjulien

  173. @samjulien Facades can* make your life easier. *when used correctly

  174. @samjulien The Power of NgRx Lies in the Architecture

  175. Reducers Components Effects NgRx @samjulien

  176. Reducers Components Effects Changing State: Actions @samjulien

  177. Reducers Components Effects Reading State: Selectors @samjulien

  178. Reducers Components Effects NgRx @samjulien

  179. Reducers Components Effects NgRx + Facades Facades @samjulien

  180. @samjulien “Prefer duplication over the wrong abstraction.” Sandi Metz

  181. @samjulien AHA: Avoid Hasty Abstractions Kent C. Dodds

  182. @samjulien Using facades naively robs NgRx of power and performance.

  183. @samjulien @samjulien

  184. @samjulien

  185. @samjulien

  186. @samjulien

  187. @samjulien Don’t abstract away actions.

  188. @samjulien Don’t abstract away actions. ✅ Use createSelector to compose

    state.
  189. @samjulien

  190. @samjulien http://samj.im/connect-2019

  191. @samjulien http://samj.im/connect-2019 Thank you!