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

Taming reactive node.js: Stream-oriented architecture with Nest

Taming reactive node.js: Stream-oriented architecture with Nest

The stream-oriented architectures are still very rare in the node.js world. While reactive programming becomes a first-class citizen in the front-end applications, we don’t have too many materials on how to apply some of these concepts in our server-side apps. In this talk, Kamil will show you Nest framework as well as introduce you to the world of event-driven systems, CQRS, and Event Sourcing concepts (and how to really take advantage of them).

Kamil Mysliwiec

March 11, 2019
Tweet

More Decks by Kamil Mysliwiec

Other Decks in Programming

Transcript

  1. KAMMYSLIWIEC const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]);

    hero.experience += dragon.level * 100; dragon.isAlive = false; await Promise.all([ this.heroesRepository.save(hero), this.dragonsRepository.save(dragon), ]);
  2. KAMMYSLIWIEC const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]);

    hero.experience += dragon.level * 100; dragon.isAlive = false; await Promise.all([ this.heroesRepository.save(hero), this.dragonsRepository.save(dragon), ]);
  3. KAMMYSLIWIEC const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]);

    hero.experience += dragon.level * 100; dragon.isAlive = false; await Promise.all([ this.heroesRepository.save(hero), this.dragonsRepository.save(dragon), ]);
  4. KAMMYSLIWIEC const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]);

    hero.experience += dragon.level * 100; dragon.isAlive = false; await Promise.all([ this.heroesRepository.save(hero), this.dragonsRepository.save(dragon), ]); TRANSACTION
  5. KAMMYSLIWIEC hero.experience += dragon.level * 100; dragon.isAlive = false; if

    (dragon.level >= 3) { const item = await this.itemsService.getRandomItem(); hero.items = hero.items.concat(item); }
  6. KAMMYSLIWIEC hero.experience += dragon.level * 100; dragon.isAlive = false; if

    (Math.random() < 0.3) { hero.coins += Math.floor(Math.random() * 101) + 100; }
  7. KAMMYSLIWIEC if (dragon.level >= 3) { const item = await

    this.itemsService.getRandomItem(); hero.items = hero.items.concat(item); if (Math.random() < 0.3) { hero.coins += Math.floor(Math.random() * 101) + 100; } }
  8. KAMMYSLIWIEC async execute(command: SlayDragonCommand) { const { heroId, dragonId }

    = command; const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]); hero.experience += dragon.level * 100; dragon.isAlive = false; }
  9. KAMMYSLIWIEC async execute(command: SlayDragonCommand) { const { heroId, dragonId }

    = command; const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]); hero.experience += dragon.level * 100; dragon.isAlive = false; }
  10. KAMMYSLIWIEC async execute(command: SlayDragonCommand) { const { heroId, dragonId }

    = command; const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]); hero.experience += dragon.level * 100; dragon.isAlive = false; }
  11. KAMMYSLIWIEC async execute(command: SlayDragonCommand) { const { heroId, dragonId }

    = command; const [hero, dragon] = await Promise.all([ this.heroesRepository.findOneById(heroId), this.dragonsRepository.findOneById(dragonId), ]); hero.slayDragon(dragon); hero.commit(); }
  12. KAMMYSLIWIEC slayDragon(dragon: Dragon) { this.experience += dragon.level * 100; dragon.isAlive

    = false; this.apply(new HeroSlayedDragonEvent({ heroId: this.id, dragonId: dragon.id, dragonLevel: dragon.level, expPoints: this.experience, })); }
  13. KAMMYSLIWIEC slayDragon(dragon: Dragon) { this.experience += dragon.level * 100; dragon.isAlive

    = false; this.apply(new HeroSlayedDragonEvent({ heroId: this.id, dragonId: dragon.id, dragonLevel: dragon.level, expPoints: this.experience, })); }
  14. KAMMYSLIWIEC async handle(event: HeroKilledDragonEvent) { const { heroId, dragonId, expPoints

    } = event; await Promise.all([ this.heroesRepository.update(heroId, { experience: expPoints }), this.dragonsRepository.update(dragonId, { isAlive: false }), ]); }
  15. KAMMYSLIWIEC @Saga() dropItem = (events$: Observable<IEvent>) => events$.pipe( ofType(HeroSlayedDragonEvent), filter(({

    dragonLevel }) => dragonLevel >= 3), map(({ heroId }) => new DropItemCommand({ heroId })), );
  16. KAMMYSLIWIEC @Saga() dropItem = (events$: Observable<IEvent>) => events$.pipe( ofType(HeroSlayedDragonEvent), filter(({

    dragonLevel }) => dragonLevel >= 3), map(({ heroId }) => new DropItemCommand({ heroId })), );
  17. KAMMYSLIWIEC @Saga() dropItem = (events$: Observable<IEvent>) => events$.pipe( ofType(HeroSlayedDragonEvent), filter(({

    dragonLevel }) => dragonLevel >= 3), map(({ heroId }) => new DropItemCommand({ heroId })), );
  18. KAMMYSLIWIEC @Saga() dropItem = (events$: Observable<IEvent>) => events$.pipe( ofType(HeroSlayedDragonEvent), filter(({

    dragonLevel }) => dragonLevel >= 3), map(({ heroId }) => new DropItemCommand({ heroId })), );
  19. KAMMYSLIWIEC @Saga() dropCoins = (events$: Observable<IEvent>) => merge( events$.pipe(ofType(HeroSlayedDragonEvent)), events$.pipe(ofType(HeroFoundItemEvent)),

    ).pipe( mergeMap(({ heroId }) => Math.random() >= 0.3 ? empty() : of(new DropCoinsCommand(heroId)), ), );
  20. KAMMYSLIWIEC @Saga() dropCoins = (events$: Observable<IEvent>) => merge( events$.pipe(ofType(HeroSlayedDragonEvent)), events$.pipe(ofType(HeroFoundItemEvent)),

    ).pipe( mergeMap(({ heroId }) => Math.random() >= 0.3 ? empty() : of(new DropCoinsCommand(heroId)), ), );