Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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.

Sam Julien

September 20, 2019
Tweet

More Decks by Sam Julien

Other Decks in Technology

Transcript

  1. Quantum Facades
    Why NgRx Facades are Great or Terrible
    Depending on How You Look at Them

    View Slide

  2. @samjulien
    Facades are Controversial

    View Slide

  3. @samjulien

    View Slide

  4. @samjulien

    View Slide

  5. @samjulien
    What the heck is a facade?

    View Slide

  6. Sam Julien
    @samjulien
    @samjulien

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. @samjulien

    View Slide

  12. @samjulien
    @samjulien

    View Slide

  13. @samjulien

    View Slide

  14. @samjulien
    What the heck is a facade?

    View Slide

  15. @samjulien
    @samjulien

    View Slide

  16. @samjulien

    View Slide

  17. @samjulien

    View Slide

  18. @samjulien

    View Slide

  19. @samjulien
    @samjulien

    View Slide

  20. @samjulien
    @samjulien

    View Slide

  21. @samjulien
    Facades in Code

    View Slide

  22. @samjulien
    The Facade Pattern

    View Slide

  23. @samjulien
    The Facade Pattern

    View Slide

  24. @samjulien
    Client
    Service A
    Service B
    Service C

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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();
    }⠀
    }⠀

    View Slide

  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();
    }⠀
    }⠀

    View Slide

  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();
    }
    }

    View Slide

  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();
    }
    }

    View Slide

  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();
    }⠀
    }⠀

    View Slide

  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();
    }⠀
    }⠀

    View Slide

  40. @samjulien

    View Slide

  41. @samjulien

    View Slide

  42. @samjulien
    Client
    Service A
    Service B
    Service C

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  55. @samjulien
    Facades in NgRx

    View Slide

  56. @samjulien

    View Slide

  57. @samjulien
    @samjulien

    View Slide

  58. @samjulien
    @samjulien

    View Slide

  59. @samjulien

    View Slide

  60. @samjulien

    View Slide

  61. Reducers
    Components
    Effects
    NgRx
    @samjulien

    View Slide

  62. Reducers
    Components
    Effects
    Changing State: Actions
    @samjulien

    View Slide

  63. @samjulien

    View Slide

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

    View Slide

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

    View Slide

  66. @samjulien
    export const startBeamOne = createAction(
    '[Beam One] Start Beam One'
    );

    View Slide

  67. Reducers
    Components
    Effects
    Reading State: Selectors
    @samjulien

    View Slide

  68. @samjulien

    View Slide

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

    View Slide

  70. @samjulien
    store.select(selectBeamOneStatus);

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  80. Reducers
    Components
    Effects
    NgRx
    @samjulien

    View Slide

  81. Reducers
    Components
    Effects
    NgRx + Facades
    Facades
    @samjulien

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  94. @samjulien

    View Slide

  95. @samjulien

    View Slide

  96. @samjulien
    Facades are awesome!

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  101. @samjulien

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  108. @samjulien
    “Can you make one change?
    It’ll be really easy.”
    Your Boss (Probably)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  112. @samjulien
    @Component({...})
    export class ColliderComponent {
    beamOneStatus$: Observable;
    beamTwoStatus$: Observable;
    constructor(
    private beamOneFacade: BeamOneFacade,
    private beamTwoFacade: BeamTwoFacade
    ) {
    this.beamOneStatus$ = this.beamOneFacade.beamOneStatus$;
    this.beamTwoStatus$ = this.beamTwoFacade.beamTwoStatus$;
    }⠀
    turnOnBeams() {
    this.beamOneFacade.turnOn();
    this.beamTwoFacade.turnOn();
    }⠀
    }⠀

    View Slide

  113. @samjulien
    We’re reusing actions!

    View Slide

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

    View Slide

  115. @samjulien

    View Slide

  116. @samjulien

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  129. @samjulien
    [Collider] Start Both Beams

    View Slide

  130. @samjulien
    Let Effects & Reducers
    Manage State

    View Slide

  131. @samjulien
    How do we fix this?

    View Slide

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

    View Slide

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

    View Slide

  134. @samjulien
    @Injectable({...})
    export class BeamOneFacade {
    beamOneStatus$: Observable;
    constructor(private store: Store) {
    this.beamOneStatus$ = this.store.select(selectBeamOneStatus);
    }
    dispatch(action: Action) {
    this.store.dispatch(action);
    }
    }

    View Slide

  135. @samjulien

    View Slide

  136. @samjulien

    View Slide

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

    View Slide

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

    View Slide

  139. @samjulien
    What about those selectors?

    View Slide

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

    View Slide

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

    View Slide

  142. @samjulien
    “Can you make one more
    change? It’ll be SUPER easy.”
    Your Boss (Again)

    View Slide

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

    View Slide

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

    View Slide

  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)
    );
    }

    View Slide

  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)
    );
    }

    View Slide

  147. @samjulien
    We’ve introduced a
    performance

    View Slide

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

    View Slide

  149. @samjulien
    combineLatest()

    View Slide

  150. @samjulien
    combineLatest()
    a

    View Slide

  151. @samjulien
    combineLatest()
    a
    1
    a1

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  155. @samjulien
    How do we fix this?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  171. @samjulien

    View Slide

  172. @samjulien

    View Slide

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

    View Slide

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

    View Slide

  175. Reducers
    Components
    Effects
    NgRx
    @samjulien

    View Slide

  176. Reducers
    Components
    Effects
    Changing State: Actions
    @samjulien

    View Slide

  177. Reducers
    Components
    Effects
    Reading State: Selectors
    @samjulien

    View Slide

  178. Reducers
    Components
    Effects
    NgRx
    @samjulien

    View Slide

  179. Reducers
    Components
    Effects
    NgRx + Facades
    Facades
    @samjulien

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  183. @samjulien
    @samjulien

    View Slide

  184. @samjulien

    View Slide

  185. @samjulien

    View Slide

  186. @samjulien

    View Slide

  187. @samjulien
    Don’t abstract away actions.

    View Slide

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

    View Slide

  189. @samjulien

    View Slide

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

    View Slide

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

    View Slide