---
title: Define an event source
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/event-sources/defining.mdx
description: Declare an event source and its event streams as a C# type, choose their names and concurrency dimensions, and check what Chronicle registered.
---

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


Use this page to declare an event source definition. To write events through it, read [Append through a definition](/chronicle/event-sources/appending/).

## Declare the definition

A definition is a class that implements `IEventSource`. Put `[EventSource]` on it, add one `[EventStream]` for each stream, and leave the body empty.

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

```csharp
using Cratis.Chronicle.EventSources;

[EventSource("Account", Description = "A customer's bank account", Concurrency = ConcurrencyDimensions.EventSourceId)]
[EventStream("Onboarding", Description = "Opening the account and verifying the customer")]
[EventStream(
    "Transactions",
    Description = "Deposits and withdrawals, one stream per month",
    Concurrency = ConcurrencyDimensions.EventSourceId | ConcurrencyDimensions.EventStreamType | ConcurrencyDimensions.EventStreamId)]
public class AccountEventSource : IEventSource;
```

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

</TabItem>
</Tabs>

| Attribute | Property | Meaning |
| --- | --- | --- |
| `[EventSource]` | name (constructor) | The event source type written on every event appended through the definition |
| | `Description` | Shown in the Workbench |
| | `Concurrency` | The default [concurrency dimensions](/chronicle/event-sources/concurrency/) for every stream |
| `[EventStream]` | name (constructor) | The event stream type written on events appended to the stream |
| | `Description` | Shown in the Workbench |
| | `Concurrency` | The dimensions for this stream. When none, the event source's dimensions apply |

Chronicle never creates an instance of the class. It reads the attributes and registers the result.

## Choose the names

The two names are what end up on stored events and what observers match on, so choose them as you would an event type name and keep them stable.

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

```csharp
using Cratis.Chronicle.EventSources;

// The event source type is "Loan": an explicit name wins.
[EventSource("Loan")]
public class BankLoanDefinition : IEventSource;

// The event source type is "ShoppingCart": the "EventSource" suffix is removed from the type name.
[EventSource(Description = "A customer's shopping cart")]
public class ShoppingCartEventSource : IEventSource;

// The attribute is optional. The event source type is "Invoice", with no description and no concurrency dimensions.
public class InvoiceEventSource : IEventSource;
```

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

</TabItem>
</Tabs>

- An explicit name in `[EventSource("Loan")]` wins.
- Without one, the name is the type name with a trailing `EventSource` removed. A type that is itself called `EventSource` keeps its name.
- The attribute is optional. Without it the definition has no description and declares no concurrency dimensions.
- Names are compared exactly. `Account` and `account` are different event source types.

Two definitions with the same name make discovery throw `DuplicateEventSourceName`. A definition that declares the same stream twice throws `DuplicateEventStreamName`.

## Choose the concurrency dimensions

`ConcurrencyDimensions` is a flags enum. It says which values of an append take part in its concurrency check.

| Flag | The guard covers events with the same |
| --- | --- |
| `EventSourceId` | event source id |
| `EventSourceType` | event source type |
| `EventStreamType` | event stream type |
| `EventStreamId` | event stream id |
| `None` | Nothing is declared. The append keeps the guard it would have had without a definition |

A stream that declares dimensions uses them. A stream that declares none inherits the event source's. [Concurrency](/chronicle/event-sources/concurrency/) shows what each combination conflicts with.

## Choose the stream ids

A stream declares a type only. The stream id is a value you pass on each append, and Chronicle does not impose a format or a template for it. Keep the convention in one place so every writer produces the same string:

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

```csharp
using Cratis.Chronicle.Events;

// Chronicle does not impose a format for an event stream id. Keep the convention in one place.
public static class TransactionStreams
{
    public static EventStreamId ForMonth(DateOnly date) => new($"{date.Year:0000}-{date.Month:00}");
}
```

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

</TabItem>
</Tabs>

Streams that are not instances of anything, such as an `Onboarding` stream that exists once per account, can use the default stream id by leaving it out.

## Where Chronicle finds definitions

The client discovers every non-abstract, non-generic class that implements `IEventSource` in the application's assemblies, together with its other artifacts. With [explicit artifact registration](/chronicle/clients/dotnet/explicit-artifacts/) you nominate definitions in the composition root instead. Registration with the kernel happens when the event store connects. [How it works](/chronicle/event-sources/how-it-works/#discovery-and-registration) has the sequence.

## Check what is registered

Read the definition back from the client:

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

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

public static class DefinitionInspector
{
    public static IEnumerable<string> DescribeStreams(IEventStore eventStore)
    {
        var definition = eventStore.EventSources.GetFor(typeof(AccountEventSource));

        // ConcurrencyFor returns the stream's dimensions when it declares any, otherwise the event source's.
        return definition.Streams.Select(stream => $"{definition.Name}/{stream.Name}: {definition.ConcurrencyFor(stream)}");
    }
}
```

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

</TabItem>
</Tabs>

`IEventStore.EventSources` is available after the event store has discovered its artifacts. The Workbench lists the registered definitions of an event store on its **Event Sources** page: name, description, owner, default concurrency and streams, with a detail pane for each stream's own dimensions. See [Workbench](/chronicle/workbench/).

## Next steps

- [Append through a definition](/chronicle/event-sources/appending/)
- [Concurrency](/chronicle/event-sources/concurrency/)
- [Migrate from string routing](/chronicle/event-sources/migrating/)
- [Dynamic Consistency Boundary in Chronicle](/chronicle/dynamic-consistency-boundary/chronicle/): where a definition fits a decision model and an append condition.
