question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Expect sequence of events in saga

See original GitHub issue

I’m submitting a…


[ ] Regression 
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

The docs state for sagas that a saga “can combine, merge, filter […] events streams.”

Does that mean that a saga could trigger a command after a sequence of events? How to do that though? How to monitor different types of events, determine if they’re related to one another somehow, and trigger the command only once two or more related events are found in the stream?

Expected behavior

There should be an example of combining two events into a stream and if possible, establishing a simple relation between them. More complex business logic (more events, more complex relations between the events) could extrapolate from that.

I would contribute to docs, if I actually knew the answers to the above questions.

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

As hinted in issues like nestjs/cqrs#64, the distinction between a saga and an event handler is kind of merky… I for one thought that a saga differs exactly in that it can watch for related events, whereas an event handler is specifically for one event at a time.

If that’s correct, the docs should give an example demonstrating this rather than simply an alternative to an event handler. If not correct, there should be a minimal example that illustrates where one should use a handler vs. where to use a saga.

Environment


Nest version: latest

 
For Tooling issues:
- Node version: latest  
- Platform: all 

Others:

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:4
  • Comments:10

github_iconTop GitHub Comments

2reactions
xdavecommented, Jan 21, 2020

I came up with this… maybe it’ll be useful for someone.

import { Type } from '@nestjs/common';
import { IEvent, ofType } from '@nestjs/cqrs';
import { pipe } from 'rxjs';
import { filter, groupBy, mergeMap, scan } from 'rxjs/operators';

export const scanEventsBy = <T extends IEvent>(key: keyof T, n: number) =>
    pipe(
        scan<T, Record<string, T>>(
            (events, event) =>
                Object.assign(events, {
                    [`${event[key]}`]: event,
                }),
            {},
        ),
        filter(events => Object.keys(events).length === n),
    );

export const groupEventsBy = <T extends IEvent>(
    key: keyof T,
    using: keyof T,
    ...eventTypes: Array<Type<T>>
) =>
    pipe(
        ofType(...eventTypes),
        groupBy(event => event[key]),
        mergeMap(group => group.pipe(scanEventsBy(using, eventTypes.length))),
    );

Example usage:

// Some events
export class TestEvent1 {
    eventName = this.constructor.name;
    aggregateId = 'test1';
}

export class TestEvent2 {
    eventName = this.constructor.name;
    aggregateId = 'test1';
}
@Injectable()
class SomeSagaClass {
    @Saga()
    test = (event$: Observable<IEvent>) =>
        event$.pipe(
            groupEventsBy('aggregateId', 'eventName', TestEvent1, TestEvent2),
            flatMap(events => [
                new DoSomethingForEvent1Command(events.TestEvent1.aggregateId),
                new DoSomethingForEvent2Command(events.TestEvent2.aggregateId),
            ]),
        );
}
// Somewhere else, example
... this.eventBus.publishAll([new TestEvent1(), new TestEvent2()]);
1reaction
boenrobotcommented, Jan 7, 2020

The example at https://youtu.be/c8hvW14VdkY?t=1147 is an interesting one… Though it still doesn’t look like it’s any better than having something like

@EventsHandler(HeroSlayedDragonEvent, HeroFoundItemEvent)
class DropCoins implements IEventHandler<HeroSlayedDragonEvent | HeroFoundItemEvent> {

    public constructor(private commandBus: CommandBus) {}

    public handle(event: HeroSlayedDragonEvent | HeroFoundItemEvent) {
        if (Math.random() >= 0.3) {
            this.commandBus.execute(new DropCoinsCommand(event.heroId));
        }
    }
}

The required boilerplate is pretty much the same as in a saga, and in either way, each occurrence of either event triggers the same code (in this case, a Math.random() call based on which a command is executed).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Pulling future actions - Redux-Saga
The loginFlow Saga more clearly conveys the expected action sequence. It knows that the LOGIN action should always be followed by a LOGOUT...
Read more >
Pattern: Saga - Microservice Architecture
A saga is a sequence of local transactions. Each local transaction updates the database and publishes a message or event to trigger the...
Read more >
Efficient Test Cases for Redux-Saga - Innominds
The expect Saga function takes saga as the first parameter, any additional arguments to expectSaga will become arguments to the saga ...
Read more >
How do I sequence actions in RxJS/redux-observable vs ...
Between sagas (generators) and epics (observables), it's important to change the way you think about how events arrive at your code.
Read more >
FixtureExecutionResult (Axon Framework Test Fixtures 1.1 API)
Expect the given number of Sagas to be active (i.e. ready to respond to ... Assert that the saga published events on the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found