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

Taming reactive node.js: Stream-oriented archit...

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

Transcript

69 slides
  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)), ), );

More Decks by Kamil Mysliwiec

Other Decks in Programming