Platform: RFC: Add CorrelationId Utilities to ngrx/effects

Created on 5 Feb 2019  路  6Comments  路  Source: ngrx/platform

Currently, @ngrx/effects does not have any support for the correlation id pattern. It would be great if there were utilities that help streamline the code.

Scenario

Inside our app, it's often necessary to dispatch an action, wait for another action in response and then continue with follow-up logic.

Example:

  1. Dispatch actionA
  2. Wait for actionB
  3. Continue and decide what to do based on actionB

Because our app needs to rely on generic stores, it's not feasible to implement each flow with special actions which can be easily filtered for, so we have correlationIds. This is a known pattern, but we have to implement the things around it on our own.

Proposed solution

It would be great if there utilities inside the @ngrx/effects package:

A method that makes it easy adding a correlationId to an action

const myAction = {type: 'MY_ACTION'};
const actionWithCorrelationId = withCorrelationId(myAction);
// => {type: 'MY_ACTION', _ngrx_correlation_id: 'some correlation id'}

A custom pipe operator that waits for correlated actions

filterActionsWithCorrelationId(actionWithCorrelationId: {type: string; _ngrx_correlation_id: string}, actionTypesToWaitFor: string[]});

An additional @Effect-property that adds correlationId of incoming action to outcoming actions

@Effect({withCorrelationId: true})
someEffect$ = this.actions$.pipe(
   filter(action => action.type === 'SOME_ACTION'), // action has _ngrx_correlationId="abc" set
   map(() => ({type: 'SOME_OUTGOING_ACTION'})) // outgoing action also has _ngrx_correlationId="abc" set
);

Alternatively, you could also decide to always add the correlationId, so there is no property required.

Edit: I'm not sure this is possible to implement, the effects library somehow has to know not only what comes out of the stream but also what goes into it.

Usage of all combined would be something like this:

@Effect()
doSth$ = this.actions$.pipe(
   filter(action => action.type === 'ACTION_A'),
   switchMap(() => {
      const outgoingAction = withCorrelationId({type: 'SOME_ACTION'});
      const responseAction$ = this.actions$.pipe(
         filterActionsWithCorrelationId(outgoingAction, ['SOME_RESPONSE_ACTION']),
         map(responseAction => {
               // responseAction is SOME_RESPONSE_ACTION
        })
     );

     return merge(of(outgoingAction), responseAction$));
  })
);

@Effect({withCorrelationId: true})
someEffect$ = this.actions$.pipe(
   filter(action => action.type === 'SOME_ACTION'),
   map(() => ({type: 'SOME_RESPONSE_ACTION'}))
);

If accepted, I would be willing to submit a PR for this feature

[x] Yes (Assistance is provided if you need help submitting a pull request)
[ ] No

Effects enhancement

All 6 comments

Closing as we won't be adding this to effects

Why? IT IS Common usecase. You have it in NGRX/Data as well

@brandonroberts It would be nice to know if you would be adding this to a separate library? or what's the plan around it?

in my opinion, you should have been able to create this functionality with something like :

// custom action creator
createActionWithCorrelationId(...) { return createAction(.../add props and corrId/.. }

// custom effect creator
createCorrelatedEffect() { .. it has to take correlatino ID from input to observable stream and add it to output actions.. }

submitData$ = createCorrelatedEffect(() =>
this.actions$.pipe(
ofType(actionCreatedByActionWithCorrelationIDCreator),
....,
// effect has to take this output (wrap observable) and add correlation ID.
map(x=> OutputActionWithCorrelationId())
));

FWIW, I implemented correlation at ngrx-etc.
You can see if this fits your needs, be aware that this is untested code (that's why it isn't documented)

FWIW, I implemented correlation at ngrx-etc.
You can see if this fits your needs, be aware that this is untested code (that's why it isn't documented)

Unfortunately action created with this Is not handled correctly in standard effect :/ not catched.

Was this page helpful?
0 / 5 - 0 ratings