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

Lightweight Architectures: With Angular's Latest Innovations

Lightweight Architectures: With Angular's Latest Innovations

Manfred Steyer

May 09, 2023
Tweet

More Decks by Manfred Steyer

Other Decks in Programming

Transcript

  1. @ManfredSteyer
    Lightweight Architectures
    With Angular's Latest Innovations
    ManfredSteyer

    View full-size slide

  2. @ManfredSteyer

    View full-size slide

  3. @ManfredSteyer

    View full-size slide

  4. @ManfredSteyer

    View full-size slide

  5. @ManfredSteyer

    View full-size slide

  6. @ManfredSteyer

    View full-size slide

  7. @ManfredSteyer
    Manfred Steyer

    View full-size slide

  8. @ManfredSteyer

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  12. @ManfredSteyer
    It looks like you want to use
    NgIfDirective and MyComponent.
    Shall I import it for you?

    View full-size slide

  13. @ManfredSteyer

    View full-size slide

  14. @ManfredSteyer

    View full-size slide

  15. @ManfredSteyer

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. @ManfredSteyer
    Restricting Access b/w Domains, etc.
    on a folder basis
    Credits to:
    Rainer Hahnekamp,
    AngularArchitects
    @softarc/eslint-plugin-sheriff

    View full-size slide

  21. @ManfredSteyer

    View full-size slide

  22. @ManfredSteyer

    View full-size slide

  23. @ManfredSteyer

    View full-size slide

  24. @ManfredSteyer

    View full-size slide

  25. @ManfredSteyer

    @Component({
    standalone: true,
    selector: 'app-root',
    imports: [
    […]
    TicketsModule,
    ],
    templateUrl: '…'
    })
    export class AppComponent {
    }

    View full-size slide

  26. @ManfredSteyer

    @Component({
    standalone: true,
    selector: 'app-root',
    imports: [
    […]
    TicketsModule,
    ],
    templateUrl: '…'
    })
    export class AppComponent {
    }

    View full-size slide

  27. @ManfredSteyer

    @NgModule({
    imports: [
    FlightCardComponent,
    ],
    declarations: [
    MyTicketsComponent
    ],
    })
    export class TicketsModule { }

    View full-size slide

  28. @ManfredSteyer

    @NgModule({
    imports: [
    FlightCardComponent,
    ],
    declarations: [
    MyTicketsComponent
    ],
    })
    export class TicketsModule { }

    View full-size slide

  29. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    MyGlobalService,
    importProvidersFrom(MyModule),
    ]
    });

    View full-size slide

  30. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    MyGlobalService,
    importProvidersFrom(MyModule),
    ]
    });

    View full-size slide

  31. @ManfredSteyer

    View full-size slide

  32. @ManfredSteyer
    ng g @angular/core:standalone

    View full-size slide

  33. @ManfredSteyer

    View full-size slide

  34. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    […]
    ]
    });

    View full-size slide

  35. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    importProvidersFrom(HttpClientModule),
    importProvidersFrom(RouterModule.forRoot(APP_ROUTES)),
    ]
    });

    View full-size slide

  36. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    provideHttpClient(),
    provideRouter(APP_ROUTES),
    ]
    });

    View full-size slide

  37. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    provideHttpClient(
    withInterceptors([authInterceptor]),
    ),
    provideRouter(APP_ROUTES,
    withPreloading(PreloadAllModules),
    withDebugTracing(),
    ),
    ]
    });

    View full-size slide

  38. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    provideLogger(loggerConfig),
    ]
    });

    View full-size slide

  39. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    provideLogger(loggerConfig,
    withColor({ debug: 3 })
    ),
    ]
    });

    View full-size slide

  40. @ManfredSteyer

    View full-size slide

  41. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    provideLogger(loggerConfig,
    withColor({ debug: 3 })
    ),
    ]
    });

    View full-size slide

  42. @ManfredSteyer
    export function provideLogger(config: LoggerConfig): Provider[] {
    […]
    }

    View full-size slide

  43. @ManfredSteyer
    export function provideLogger(config: LoggerConfig): Provider[] {
    return [
    LoggerService,
    {
    provide: LoggerConfig,
    useValue: config
    },
    {
    provide: LogFormatter,
    useValue: config.formatter
    },
    ];
    }

    View full-size slide

  44. @ManfredSteyer
    export function provideLogger(config: LoggerConfig): Provider[] {
    return [
    LoggerService,
    {
    provide: LoggerConfig,
    useValue: config
    },
    {
    provide: LogFormatter,
    useValue: config.formatter
    },
    ];
    }

    View full-size slide

  45. @ManfredSteyer
    export function provideLogger(config: LoggerConfig): EnvironmentProviders {
    return makeEnvironmentProviders([
    LoggerService,
    {
    provide: LoggerConfig,
    useValue: config
    },
    {
    provide: LogFormatter,
    useValue: config.formatter
    },
    ]);
    }

    View full-size slide

  46. @ManfredSteyer
    export function provideLogger(
    config: LoggerConfig, ...featues: LoggerFeature[]
    ): EnvironmentProviders {
    return makeEnvironmentProviders([
    LoggerService,
    […],
    features?.map(f => f.providers)
    ]);
    }

    View full-size slide

  47. @ManfredSteyer

    View full-size slide

  48. @ManfredSteyer
    export const APP_ROUTES: Routes = [
    […]
    {
    path: 'flight-booking',
    canActivate: [() => inject(AuthService).isAuthenticated()],
    component: FlightBookingComponent
    },
    ]

    View full-size slide

  49. @ManfredSteyer
    export const APP_ROUTES: Routes = [
    […]
    {
    path: 'flight-booking',
    canActivate: [() => inject(AuthService).isAuthenticated()],
    component: FlightBookingComponent
    },
    ]

    View full-size slide

  50. @ManfredSteyer
    export const APP_ROUTES: Routes = [
    […]
    {
    path: 'flight-booking',
    canActivate: [() => inject(AuthService).isAuthenticated()],
    resolve: {
    flights: () => inject(FlightService).findAll()
    },
    component: FlightBookingComponent
    },
    ]

    View full-size slide

  51. @ManfredSteyer
    export const authInterceptor: HttpInterceptorFn = (req, next) => {
    […]
    }

    View full-size slide

  52. @ManfredSteyer
    export const authInterceptor: HttpInterceptorFn = (req, next) => {
    if (req.url.startsWith('https://[…]/')) {
    const headers = req.headers.set('Authorization', 'Bearer 12345');
    req = req.clone({headers});
    }
    […]
    }

    View full-size slide

  53. @ManfredSteyer
    export const authInterceptor: HttpInterceptorFn = (req, next) => {
    if (req.url.startsWith('https://[…]/')) {
    const headers = req.headers.set('Authorization', 'Bearer 12345');
    req = req.clone({headers});
    }
    return next(req);
    }

    View full-size slide

  54. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    provideHttpClient(
    withInterceptors([authInterceptor]),
    ),
    ]
    });

    View full-size slide

  55. @ManfredSteyer
    bootstrapApplication(AppComponent, {
    providers: [
    provideLogger({
    level: LogLevel.DEBUG,
    appenders: [DefaultLogAppender],
    formatter: (level, cat, msg) => [level, cat, msg].join(';'),
    }),
    ]
    });

    View full-size slide

  56. @ManfredSteyer
    Free eBook (brand new)
    ANGULARarchitects.io/standalone
    Standalone Components
    Download here:

    View full-size slide

  57. @ManfredSteyer

    View full-size slide

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

    View full-size slide

  59. @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 full-size slide

  60. @ManfredSteyer
    Signals!

    View full-size slide

  61. @ManfredSteyer
    Signals!

    View full-size slide

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

    View full-size slide

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



    View full-size slide

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



    View full-size slide

  65. @ManfredSteyer
    Fine-grained
    and Zone-less
    CD
    Convertible to
    Observables
    and vice versa!
    No need to
    unsubscribe!
    No need to
    update code!

    View full-size slide

  66. @ManfredSteyer

    View full-size slide

  67. @ManfredSteyer
    Modern Angular:
    Lightweight
    Architectures
    Standalone
    Components
    and APIs
    Functional Services
    Upcoming:
    Signals

    View full-size slide

  68. @ManfredSteyer

    View full-size slide

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

    View full-size slide