---
title: Event Sequence
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/reducers/event-sequence.mdx
description: By default, reducers observe the default event log. You can override this by specifying a different event sequence using either the [EventSequence]…
---

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

By default, reducers observe the default event log. You can override this by specifying a different event sequence using either the `[EventSequence]` attribute or the `eventSequence` parameter on the `[Reducer]` attribute.

For a [self reducer](/chronicle/reducers/self-reducing-read-models/), place sequence configuration on the model itself. It has the same sequence selection and external-store routing rules as a classic reducer; a sequence attribute alone does not make it a model-bound projection.

## Using the `[EventSequence]` Attribute

Apply `[EventSequence]` directly to your reducer class to pin it to a specific event sequence:

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

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

[EventType]
public record ReducersEventSequenceShipmentDispatched(string TrackingNumber, string Carrier);

public record ReducersEventSequenceShipmentSummary(string TrackingNumber, string Carrier, DateTimeOffset DispatchedAt);

[EventSequence("fulfillment-events")]
public class ReducersEventSequenceShipmentSummaryReducer : IReducerFor<ReducersEventSequenceShipmentSummary>
{
    public ReducersEventSequenceShipmentSummary Dispatched(ReducersEventSequenceShipmentDispatched @event, ReducersEventSequenceShipmentSummary? current, EventContext context) =>
        new(@event.TrackingNumber, @event.Carrier, context.Occurred);
}
```

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

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.observation.EventSequence
import io.cratis.chronicle.observation.Reducer
import io.cratis.chronicle.readModels.ReadModel

@EventType
data class ReducersEventSequenceRefundIssued(val amount: Double)

@ReadModel
data class ReducersEventSequenceRefundSummary(val refunded: Double = 0.0)

@Reducer
@EventSequence("outbox")
class ReducersEventSequenceRefundSummaryReducer {
    fun issued(
        event: ReducersEventSequenceRefundIssued,
        current: ReducersEventSequenceRefundSummary?
    ): ReducersEventSequenceRefundSummary =
        ReducersEventSequenceRefundSummary((current?.refunded ?: 0.0) + event.amount)
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/reducers/event-sequence/event-sequence-attribute.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.events.EventType;
import io.cratis.chronicle.observation.EventSequence;
import io.cratis.chronicle.observation.Reducer;
import io.cratis.chronicle.readModels.ReadModel;

@EventType
record ReducersEventSequenceRefundIssued(double amount) {}

@ReadModel
record ReducersEventSequenceRefundSummary(double refunded) {
    ReducersEventSequenceRefundSummary() {
        this(0.0);
    }
}

@Reducer
@EventSequence("outbox")
class ReducersEventSequenceRefundSummaryReducer {
    ReducersEventSequenceRefundSummary issued(
            ReducersEventSequenceRefundIssued event,
            ReducersEventSequenceRefundSummary current) {
        double refunded = current == null ? 0.0 : current.refunded();
        return new ReducersEventSequenceRefundSummary(refunded + event.amount());
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/reducers/event-sequence/event-sequence-attribute.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/reducers/event-sequence/event-sequence-attribute.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { eventType, reducer } from '@cratis/chronicle';
import { field } from '@cratis/fundamentals';

@eventType()
class EventSequenceShipmentDispatched {
    @field(String) readonly trackingNumber: string;

    constructor(trackingNumber: string) {
        this.trackingNumber = trackingNumber;
    }
}

class EventSequenceShipmentStatus {
    trackingNumber = '';
}

@reducer('', 'fulfillment-events', EventSequenceShipmentStatus)
class EventSequenceShipmentReducer {
    eventSequenceShipmentDispatched(
        event: EventSequenceShipmentDispatched,
        current: EventSequenceShipmentStatus | undefined
    ): EventSequenceShipmentStatus {
        return { trackingNumber: event.trackingNumber };
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/reducers/event-sequence/event-sequence-attribute.md)

</TabItem>
</Tabs>

TypeScript selects the sequence through the `reducer()` decorator's parameter. Kotlin and Java can
use either `@EventSequence` or the `eventSequence` parameter on `@Reducer`. Use the client tab's
configuration shape; the parameter-based form is shown below.

## Using the `[Reducer]` Attribute

When you are already using the `[Reducer]` attribute to set a custom identifier or other options, use its `eventSequence` parameter instead of adding a separate `[EventSequence]` attribute:

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

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

[EventType]
public record ReducersEventSequenceReducerAttributeShipmentDispatched(string TrackingNumber, string Carrier);

public record ReducersEventSequenceReducerAttributeShipmentSummary(string TrackingNumber, string Carrier, DateTimeOffset DispatchedAt);

[Reducer(id: "shipment-summary", eventSequence: "fulfillment-events")]
public class ReducersEventSequenceReducerAttributeShipmentSummaryReducer : IReducerFor<ReducersEventSequenceReducerAttributeShipmentSummary>
{
    public ReducersEventSequenceReducerAttributeShipmentSummary Dispatched(ReducersEventSequenceReducerAttributeShipmentDispatched @event, ReducersEventSequenceReducerAttributeShipmentSummary? current, EventContext context) =>
        new(@event.TrackingNumber, @event.Carrier, context.Occurred);
}
```

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

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.events.EventType
import io.cratis.chronicle.observation.Reducer
import io.cratis.chronicle.readModels.ReadModel

@EventType
data class ReducersEventSequencePaymentSettled(val amount: Double)

@ReadModel
data class ReducersEventSequencePaymentSummary(val settled: Double = 0.0)

@Reducer(id = "payment-summary", eventSequence = "outbox")
class ReducersEventSequencePaymentSummaryReducer {
    fun settled(
        event: ReducersEventSequencePaymentSettled,
        current: ReducersEventSequencePaymentSummary?
    ): ReducersEventSequencePaymentSummary =
        ReducersEventSequencePaymentSummary((current?.settled ?: 0.0) + event.amount)
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/reducers/event-sequence/reducer-attribute.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.events.EventType;
import io.cratis.chronicle.observation.Reducer;
import io.cratis.chronicle.readModels.ReadModel;

@EventType
record ReducersEventSequencePaymentSettled(double amount) {}

@ReadModel
record ReducersEventSequencePaymentSummary(double settled) {
    ReducersEventSequencePaymentSummary() {
        this(0.0);
    }
}

@Reducer(id = "payment-summary", eventSequence = "outbox")
class ReducersEventSequencePaymentSummaryReducer {
    ReducersEventSequencePaymentSummary settled(
            ReducersEventSequencePaymentSettled event,
            ReducersEventSequencePaymentSummary current) {
        double settled = current == null ? 0.0 : current.settled();
        return new ReducersEventSequencePaymentSummary(settled + event.amount());
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/reducers/event-sequence/reducer-attribute.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/reducers/event-sequence/reducer-attribute.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { eventType, reducer } from '@cratis/chronicle';

@eventType()
class ReducersEventSequenceReducerAttributeShipmentDispatched {
    trackingNumber = '';
    carrier = '';
}

class ReducersEventSequenceReducerAttributeShipmentSummary {
    trackingNumber = '';
    carrier = '';
    dispatchedAt: Date = new Date(0);
}

@reducer('shipment-summary', 'fulfillment-events', ReducersEventSequenceReducerAttributeShipmentSummary)
class ReducersEventSequenceReducerAttributeShipmentSummaryReducer {
    // Method name must be the exact camelCase of the event's class name
    reducersEventSequenceReducerAttributeShipmentDispatched(
        event: ReducersEventSequenceReducerAttributeShipmentDispatched,
        _current: ReducersEventSequenceReducerAttributeShipmentSummary | undefined
    ): ReducersEventSequenceReducerAttributeShipmentSummary {
        return { trackingNumber: event.trackingNumber, carrier: event.carrier, dispatchedAt: new Date() };
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/reducers/event-sequence/reducer-attribute.md)

</TabItem>
</Tabs>

Both approaches produce the same result. Prefer `[Reducer(eventSequence: ...)]` when you are already customizing the reducer with other parameters on that attribute.

## Convenience: `[EventLog]`

Use `[EventLog]` when you want to be explicit that the reducer reads from the default event log, even when event types in the assembly carry a `[EventStore]` attribute pointing elsewhere:

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

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

[EventType]
public record ReducersEventSequenceLogOrderPlaced(Guid OrderId);

public record ReducersEventSequenceLocalOrderSummary(int OrderCount, DateTimeOffset LastOrderAt);

[EventLog]
public class ReducersEventSequenceLocalOrderSummaryReducer : IReducerFor<ReducersEventSequenceLocalOrderSummary>
{
    public ReducersEventSequenceLocalOrderSummary Placed(ReducersEventSequenceLogOrderPlaced @event, ReducersEventSequenceLocalOrderSummary? current, EventContext context)
    {
        var count = current?.OrderCount ?? 0;
        return new ReducersEventSequenceLocalOrderSummary(count + 1, context.Occurred);
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/reducers/event-sequence/event-log-attribute.md)

</TabItem>
<TabItem label="Kotlin">

```text
Kotlin does not support this workflow yet.
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/reducers/event-sequence/event-log-attribute.md)

</TabItem>
<TabItem label="Java">

```text
Java does not support this workflow yet.
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/reducers/event-sequence/event-log-attribute.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/reducers/event-sequence/event-log-attribute.md)

</TabItem>
<TabItem label="TypeScript">

```typescript
import { EventContext, eventType, Guid, reducer } from '@cratis/chronicle';
import { field } from '@cratis/fundamentals';

@eventType()
class EventSequenceLogOrderPlaced {
    @field(Guid) readonly orderId: Guid;

    constructor(orderId: Guid) {
        this.orderId = orderId;
    }
}

class EventSequenceLogOrderAudit {
    orderId: Guid = Guid.empty;
}

// No eventSequenceId given - observes the default event log
@reducer('', undefined, EventSequenceLogOrderAudit)
class EventSequenceLocalAuditReducer {
    eventSequenceLogOrderPlaced(
        event: EventSequenceLogOrderPlaced,
        current: EventSequenceLogOrderAudit | undefined,
        context: EventContext
    ): EventSequenceLogOrderAudit {
        return { orderId: event.orderId };
    }
}
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/reducers/event-sequence/event-log-attribute.md)

</TabItem>
</Tabs>

`[EventLog]` explicitly selects the default log instead of inferring an inbox from event-store
metadata. Use the event-log sequence name when configuring the same selection through a sequence parameter.

## External Event Store Inbox Routing

For outbox-to-inbox subscriptions between event stores, including automatic inbox routing and observer-level `[EventStore]` usage, see [Subscribe Reducers to External Event Stores](/chronicle/reducers/external-event-store-subscriptions/).

## When to Use `[EventSequence]` Explicitly

- When you need to read from a specific sequence other than the inferred one
- To suppress automatic inbox routing for a reducer that handles foreign events
- For reducers that target a specialized or partitioned event stream
- When an explicit sequence name improves readability and makes intent clear
