$30 off During Our Annual Pro Sale. View Details »

Lightweight Architectures: With Angular's Signals and Standalone

Lightweight Architectures: With Angular's Signals and Standalone

Manfred Steyer
PRO

September 19, 2023
Tweet

More Decks by Manfred Steyer

Other Decks in Programming

Transcript

  1. @ManfredSteyer
    Lightweight Architectures
    With Angular's Signals and Standalone
    ManfredSteyer

    View Slide

  2. @ManfredSteyer

    View Slide

  3. @ManfredSteyer

    View Slide

  4. @ManfredSteyer

    View Slide

  5. @ManfredSteyer

    View Slide

  6. @ManfredSteyer

    View Slide

  7. @ManfredSteyer
    Manfred Steyer

    View Slide

  8. @ManfredSteyer

    View Slide

  9. @ManfredSteyer
    NgModules + EcmaScript Modules
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppComponent } from './app.component';
    […]
    @NgModule({
    imports: [BrowserModule, OtherModule],
    declarations: [AppComponent, OtherComponent, OtherDirective],
    providers: [],
    bootstrap: [AppComponent],
    })
    export class AppModule {}
    TypeScript Modules
    Angular Modules

    View Slide

  10. @ManfredSteyer
    @Component({
    standalone: true,
    imports: [
    […],
    FlightCardComponent,
    CityPipe,
    CityValidator,
    ],
    selector: 'flight-search',
    templateUrl: '…'
    })
    export class FlightSearchComponent {
    […]
    }

    View Slide

  11. @ManfredSteyer

    View Slide

  12. @ManfredSteyer
    Small and Medium Apps:
    Folder per Feature

    View Slide

  13. @ManfredSteyer
    Your Public APIs:
    Barrels
    // index.ts == Public API
    export *
    from './flight-booking.routes';

    View Slide

  14. @ManfredSteyer
    Medium and Large Apps:
    Folder per Domain

    View Slide

  15. @ManfredSteyer
    Restricting Access b/w Domains, etc.
    on a library basis

    View Slide

  16. @ManfredSteyer
    Restricting Access
    on a folder basis
    Tech Lead
    Rainer Hahnekamp,
    AngularArchitects
    @softarc/eslint-plugin-sheriff

    View Slide

  17. @ManfredSteyer

    View Slide

  18. @ManfredSteyer
    Free eBook
    ANGULARarchitects.io/standalone
    Standalone Components
    Download here:

    View Slide

  19. @ManfredSteyer

    View Slide

  20. @ManfredSteyer
    Zone.js:
    Monkey
    Patching
    After Event
    Handler:
    Inform Angular
    CD Checks
    Components
    all components (default)
    or selected ones (OnPush)

    View Slide

  21. @ManfredSteyer
    Zone.js:
    Magic
    Zone.js:
    ~100K
    Cannot patch
    async/await
    coarse
    grained CD
    e.g. Components are always checked as a
    whole, even if only a tiny fraction changed

    View Slide

  22. @ManfredSteyer
    Signals!

    View Slide

  23. @ManfredSteyer
    Signals!

    View Slide

  24. @ManfredSteyer
    Signal
    as Producer
    4711
    Consumer
    read
    set
    notify
    4712

    View Slide

  25. @ManfredSteyer
    flights: Flight[] = [];
    const flights = await this.flightService.findAsPromise(from, to);
    this.flights = flights;



    View Slide

  26. @ManfredSteyer
    flights = signal([]);
    const flights = await this.flightService.findAsPromise(from, to);
    this.flights.set(flights);



    View Slide

  27. @ManfredSteyer

    View Slide

  28. @ManfredSteyer

    View Slide

  29. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    private _flights = signal([]);
    readonly flights = this._flights.asReadonly();
    async load(from: string, to: string) {
    const flights = await […];
    this._flights.set(flights);
    }
    }

    View Slide

  30. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    private _flights = signal([]);
    readonly flights = this._flights.asReadonly();
    async load(from: string, to: string) {
    const flights = await […];
    this._flights.set(flights);
    }
    }

    View Slide

  31. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    private _flights = signal([]);
    readonly flights = this._flights.asReadonly();
    private _from = signal('Hamburg');
    readonly from = this._from.asReadonly();
    private _to = signal('Graz');
    readonly to = this._to.asReadonly();
    […]
    }

    View Slide

  32. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    private state = signal({
    from: 'Hamburg',
    to: 'Graz',
    flights: [] as Flight[], […]
    }, { equal });
    […]
    }

    View Slide

  33. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    private state = signal({
    from: 'Hamburg',
    to: 'Graz',
    flights: [] as Flight[], […]
    }, { equal });
    flights = computed(() => this.state().flights, { equal });
    from = computed(() => this.state().from, { equal });
    […]
    }

    View Slide

  34. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    private state = signal({
    from: 'Hamburg',
    to: 'Graz',
    flights: [] as Flight[], […]
    }, { equal });
    flights = computed(() => this.state().flights, { equal });
    from = computed(() => this.state().from, { equal });
    […]
    }
    const equal = (a,b) => a === b

    View Slide

  35. @ManfredSteyer

    View Slide

  36. @ManfredSteyer
    select(selector)
    selectSignal(selector)

    View Slide

  37. @ManfredSteyer

    View Slide

  38. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    private state = signalState({
    from: 'Paris',
    to: 'London',
    flights: [] as Flight[],
    basket: {} as Record,
    });
    flights = this.state.flights;
    from = this.state.from;
    […]
    }

    View Slide

  39. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    selected = selectSignal(
    () => this.flights().filter((f) => this.basket()[f.id]);
    […]
    }

    View Slide

  40. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    selected2 = selectSignal(
    this.flights,
    this.basket,
    (flights, basket) => flights.filter((f) => basket[f.id])
    );
    […]
    }

    View Slide

  41. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    updateCriteria(from: string, to: string): void {
    this.state.$update({ from, to });
    }
    […]
    }

    View Slide

  42. @ManfredSteyer
    @Injectable({ providedIn: 'root' })
    export class FlightBookingFacade {
    […]
    updateCriteria(from: string, to: string): void {
    this.state.$update(state => ({ ...state, from, to }));
    }
    […]
    }

    View Slide

  43. @ManfredSteyer

    View Slide

  44. @ManfredSteyer
    export const FlightBookingStore = signalStore(
    { providedIn: 'root' },
    […]
    );

    View Slide

  45. @ManfredSteyer
    export const FlightBookingStore = signalStore(
    { providedIn: 'root' },
    withState({
    from: 'Paris',
    to: 'London',
    […]
    }),
    […]
    );

    View Slide

  46. @ManfredSteyer
    export const FlightBookingStore = signalStore(
    { providedIn: 'root' },
    withState({
    from: 'Paris',
    to: 'London',
    […]
    }),
    withSignals(([…]) => ({ […] })),
    withMethods(([…]) => ({ })),
    withHooks({ […] }),
    withCallState()
    );

    View Slide

  47. @ManfredSteyer

    View Slide

  48. @ManfredSteyer
    const BooksStore = signalStore(
    withEntities({ collection: 'book' }),
    withEntities({ collection: 'author' })
    );

    View Slide

  49. @ManfredSteyer
    const BooksStore = signalStore(
    withLoadEntities(BookService),
    withLoadEntities(AuthorService),
    );

    View Slide

  50. @ManfredSteyer

    View Slide

  51. @ManfredSteyer

    View Slide

  52. @ManfredSteyer
    d
    Slides & Examples Remote Company Workshops
    and Consulting
    http://angulararchitects.io

    View Slide