Skip to content

Appending with Tags

Chronicle lets you associate tags with appended events. Tags are stored as event metadata and can be used for categorization and filtering. There are two kinds: plain tags, which are simple strings, and named tags, which pair a name with a value.

The tags you add don’t take part in concurrency control. A concurrency scope narrows by the built-in metadata tags below and by event type.

Chronicle maintains built-in metadata tags for event identity and stream routing:

  • EventSourceType
  • EventSourceId
  • EventStreamType
  • EventStreamId

You can add custom tags in addition to these built-in tags. For the full list and behavior, see Event Metadata Tags.

Custom tags are simple strings that you provide when appending. They are merged with any static tags defined on the event type.

using Cratis.Chronicle.Events;
[EventType]
public record TaggedOrderPlaced(string CustomerId, decimal Total);
public class TaggedCheckoutService(IEventLog eventLog)
{
public Task<AppendResult> PlaceOrder(OrderId orderId, string customerId, decimal total)
{
return eventLog.Append(
orderId,
new TaggedOrderPlaced(customerId, total),
tags: ["checkout", "priority"]);
}
}

View C# snippet source on GitHub

AppendMany applies the provided tags to each event in the batch.

using Cratis.Chronicle.EventSequences;
using Cratis.Chronicle.Events;
public readonly record struct TaggedAccountId(string Value)
{
public static implicit operator EventSourceId(TaggedAccountId id) => new(id.Value);
}
[EventType]
public record TaggedMoneyWithdrawn(decimal Amount);
[EventType]
public record TaggedMoneyDeposited(decimal Amount);
public class TaggedTransferService(IEventLog eventLog)
{
public Task<AppendManyResult> Transfer(TaggedAccountId fromAccount, TaggedAccountId toAccount, decimal amount)
{
var events = new[]
{
new EventForEventSourceId(fromAccount, new TaggedMoneyWithdrawn(amount)),
new EventForEventSourceId(toAccount, new TaggedMoneyDeposited(amount))
};
return eventLog.AppendMany(events, tags: ["transfer", "audit"]);
}
}

View C# snippet source on GitHub

A named tag pairs a name with an exact value, for example checkout-session = c-81f2. Use one when you want to find events by a runtime identifier later. Tagging explains the matching rules and how to query by named tag.

Use AppendWithNamedTags for a tagged append; the existing Append call remains unchanged.

using Cratis.Chronicle;
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSequences;
[EventType]
public record NamedTaggedOrderPlaced(decimal Total);
public class NamedTaggedCheckoutService(IEventLog eventLog)
{
public Task<AppendResult> PlaceOrder(EventSourceId orderId, string checkoutSessionId, decimal total) =>
eventLog.AppendWithNamedTags(
orderId,
new NamedTaggedOrderPlaced(total),
namedTags:
[
new NamedTag("checkout-session", checkoutSessionId),
new NamedTag("sales-channel", "web")
],
tags: ["checkout"]);
}

View C# snippet source on GitHub

Named tags are stored separately from plain tags. You can pass both on the same append, and neither affects the other.

Use AppendManyWithNamedTags when supplying call-level named tags.

Each event in a batch can carry its own named tags, and the append call can add named tags that apply to every event. Chronicle merges the two for each event:

  • An exact duplicate of a name and value pair is kept once.
  • The same name with a different value stays as a separate tag.
using Cratis.Chronicle;
using Cratis.Chronicle.Events;
using Cratis.Chronicle.EventSequences;
[EventType]
public record NamedTaggedMoneyWithdrawn(decimal Amount);
[EventType]
public record NamedTaggedMoneyDeposited(decimal Amount);
public class NamedTaggedTransferService(IEventLog eventLog)
{
public Task<AppendManyResult> Transfer(EventSourceId fromAccount, EventSourceId toAccount, decimal amount, string transferId)
{
EventForEventSourceId[] events =
[
new(fromAccount, new NamedTaggedMoneyWithdrawn(amount)) { NamedTags = [new NamedTag("ledger-side", "debit")] },
new(toAccount, new NamedTaggedMoneyDeposited(amount)) { NamedTags = [new NamedTag("ledger-side", "credit")] }
];
// The withdrawal carries ledger-side = debit and transfer = <transferId>.
// The deposit carries ledger-side = credit and transfer = <transferId>.
return eventLog.AppendManyWithNamedTags(
events,
namedTags: [new NamedTag("transfer", transferId)],
tags: ["transfer"]);
}
}

View C# snippet source on GitHub

Transactional appends through a unit of work accept named tags the same way.

Named tags are plaintext and are not encrypted by the compliance features. Don’t put secrets or personal data in them.