Implement a simple bus event between NgRx ComponentStores
Tags: angular, webdev, tutorial, ngrx
Published: 2023-11-13 14:04:44

## What is EDA ?

The bus event refer to *event driven architecture ( EDA ).* The main purpose of the EDA is to **break coupling between services** by opposition to the *service driven architecture ( SOA ).*

In **SOA** when a consumer need a service, he directly ask to a supplier to provide this service. In this classique composition, we need to know in the consumer, which provider will resolve our issue: **We have in SOA a strong coupling between services**.

With the **EDA**, the services emit events when they make particular actions. It is the task of the other services to listen these events and to do their jobs at this moment. In this architecture the emitting service never know and dont needs to know which provider should be called.

In the EDA, we use bus events in order to transmit events. All the services will subscribe to the bus event and only listen the events they need to handle.

### Bus event implementation

Our bus event is really simple in fact. We have declared into the core module (provided in all the application) an [rxjs](https://rxjs.dev/) Subject.

```tsx
{ provide: EVENT_MESSAGE, useValue: <EventMessageBus>new Subject<DomainEvent>() }
```

From this we just need two more things to use this bus. one way to publish and one way to subscribe.

You will retrieve two services that will carry these tasks:

- The `EventHandler`, will just subscribe to the subject and filter the events you need,
- The `EventPublisher`, just publish event you give him as input.

Let’s see a small example right now to picture the concept…

### Exemple : The Toast service

We actually use a Toast service in our application. When some actions are done by our services or when we receive data from the server we want to display toasts to warn the user that some events occurs.

We have obviously created a **Toast service** that is capable to display toast when we give him a message, a title and, a status. But the main problem from this point is that, we will have to call this service everywhere in the application in order to use it.

To avoid it, the bus event will clearly help us. Instead of calling the toast service, we will just push events into the bus event. The toast service will subscribe to the relevant event and handle them by displaying a toast.

First of all, inject the event publisher into your service:

```tsx
constructor(private readonly eventPublisher: EventPublisher) {}
```

Use the publisher to publish en event into the bus when an action needs to be triggered. Here we try to update a client into the `ClientEditionStore` bu we want to handle cases there is an error that come back from the server, so we publish the `ClientEditionErrorEvent`

```tsx
readonly updateClient = this.effect((origin$: Observable<Omit<Client, 'contacts' | 'sequenceEnabled'>>) => {
    return origin$.pipe(
      switchMap((client) => this.clientsHttpService.updateClient(client.id, client)),
      catchError(() => {
        this.eventPublisher.publish(new ClientEditionErrorEvent());
      }),
    );
  });
```

Of course you will have to define this event before using it:

```tsx
export class ClientEditionErrorEvent extends DomainEvent {}
```

The `DomainEvent` is actually the super class of the event we can publish into the bus event . If you check in the first part of this guide you will retrieve this class into the Subject injection.

It is all you need to do in this service but you need to handle this event in the toast service now!

First of all inject the event handler:

```tsx
constructor(private readonly eventHandler: EventHandler) {}
```

And you can subscribe / handle to the bus event and display your toast here

```tsx
initToaster() {
    this.eventHandler
      .handle(
        ClientEditionErrorEvent,
      )
      .subscribe((event) => this.showToastByEvent(event));
  }
```

## When use a bus event ?

In our application we can use bus event in different use cases:

- To allow stores to discuss and exchange information,
- To allow application to scale without adding links between services,
- To allow an easy handling of the toasts,
- To allow an easy handling of the server errors

## Mistakes to avoid

### Do not emit events that are handled in the same service ❌

```tsx
readonly deleteStep = this.effect((origin$: Observable<{ sequenceId: string; stepId: string }>) => {
    return origin$.pipe(
      switchMap(({ sequenceId, stepId }) => this.sequenceService.deleteStep(sequenceId, stepId)),
      tapAndRetry(
        () => this.eventPublisher.publish(new DeleteStepSuccessEvent()),
        () => this.eventPublisher.publish(new DeleteStepErrorEvent()),
      ),
    );
  });x
```

```tsx
this.eventHandler
      .handle(DeleteStepSuccessEvent, SequenceUpdateEnableSuccessEvent, SequenceSettingsUpdateOrCreateSuccessEvent)
      .pipe(
        untilDestroyed(this),
        withLatestFrom(this.sequence$),
        map(([_, sequence]) => sequence),
        filterUndefined(),
        tap((sequence) => this.fetchSequence(sequence.id)),
      )
      .subscribe();
```