---
title: Filter observers by event source and stream
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/event-sources/filtering.mdx
description: Make a reactor or reducer handle only the events of one stream of an event source definition, and what CHR0055 and CHR0056 check at build time.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';


Use `[FromEventSource<TSource>("Stream")]` on a reactor or a reducer when it should handle only the events appended to one stream of a definition. The filter replaces the pair of strings in `[EventSourceType]` and `[EventStreamType]`.

## Filter a reactor

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSources;
using Cratis.Chronicle.Reactors;

[EventType]
public record MoneyDeposited(decimal Amount);

[EventSource("Account")]
[EventStream("Onboarding")]
[EventStream("Transactions")]
public class AccountEventSource : IEventSource;

// Receives MoneyDeposited only when it was appended to the Transactions stream of the Account event source.
[FromEventSource<AccountEventSource>("Transactions")]
public class StatementReactor : IReactor
{
    public Task Deposited(MoneyDeposited @event, EventContext context) => Task.CompletedTask;
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-sources/filtering/reactor.md)

</TabItem>
</Tabs>

The reactor receives `MoneyDeposited` only when it was appended to the `Transactions` stream of the `Account` event source. A deposit appended through another definition, or to the `Onboarding` stream, is skipped.

## Filter a reducer

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSources;
using Cratis.Chronicle.Reducers;

public record DepositTotal(decimal Amount);

[FromEventSource<AccountEventSource>("Transactions")]
public class DepositTotalReducer : IReducerFor<DepositTotal>
{
    public DepositTotal Deposited(MoneyDeposited @event, DepositTotal? current, EventContext context) =>
        new((current?.Amount ?? 0m) + @event.Amount);
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/event-sources/filtering/reducer.md)

</TabItem>
</Tabs>

## How the match works

The attribute resolves to two values when the observer is registered: the definition's name as the event source type, and the stream name as the event stream type. An event passes when its stored event source type and event stream type equal them. Chronicle compares the stored values, not the definition type, so an event appended with the same strings without a definition also matches.

If the definition or the stream cannot be resolved when the observer registers, the client throws `UnknownEventSource` or `EventStreamDoesNotBelongToEventSource`.

You can combine the typed filter with `[FilterEventsByTag]`. All configured categories must match. Filters apply to reactors and reducers. A projection ignores them, which [CHR0041](/chronicle/code-analysis/chr0041/) reports.

## Build-time checks

The code analyzer checks typed filters while you compile:

| Rule | Severity | Reports |
| --- | --- | --- |
| [CHR0055](/chronicle/code-analysis/chr0055/) | Error | The stream is not declared by the definition |
| [CHR0056](/chronicle/code-analysis/chr0056/) | Error | `[EventSourceType]` or `[EventStreamType]` on the same type contradicts the typed filter |

An explicit attribute that repeats the same value as the typed filter is allowed, but it adds nothing.

## Filtering by string

`[EventSourceType]` and `[EventStreamType]` still work without a definition. See [Filter by event source type](/chronicle/events/filtering/by-event-source-type/) and [Filter by event stream type](/chronicle/events/filtering/by-event-stream-type/). To move from the strings to the typed form, read [Migrate from string routing](/chronicle/event-sources/migrating/).

## Next steps

- [Scenarios](/chronicle/event-sources/scenarios/#a-process-stream): a reactor that follows one process stream.
- [Dynamic Consistency Boundary](/chronicle/event-sources/dcb/): the decision happens on the write side, and observers react afterwards.
- [Reactors](/chronicle/reactors/)
- [Dynamic Consistency Boundary in Chronicle](/chronicle/dynamic-consistency-boundary/chronicle/): what consistency covers and what observers do not.
