Tagging Projections
Tags provide a way to organize and tag your projections for better discoverability and management. By applying the [Tag] attribute to your projection classes, you can assign one or more tags that describe the purpose or domain of the projection.
[Tag] and [Tags] on projections label the projection definition. They do not filter which appended events the projection observes. Use projection event declarations and event sequence selection to control projection input, and use reducer or reactor filters when you need metadata-based filtering for appended events. See Filter reducers and reactors by appended event metadata.
Adding Tags
Section titled “Adding Tags”You can tag projections in multiple ways:
Single Tag
Section titled “Single Tag”Apply a single tag to your projection:
using Cratis.Chronicle;using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record TaggingOrderPlaced(string OrderId);
[EventType]public record TaggingItemAddedToOrder(decimal Amount);
public record TaggingOrderAnalytics(string OrderId, decimal TotalAmount);
[Tag("Analytics")]public class TaggingOrderAnalyticsProjection : IProjectionFor<TaggingOrderAnalytics>{ public void Define(IProjectionBuilderFor<TaggingOrderAnalytics> builder) => builder .From<TaggingOrderPlaced>(_ => _ .Set(m => m.OrderId).To(e => e.OrderId)) .From<TaggingItemAddedToOrder>(_ => _ .Add(m => m.TotalAmount).With(e => e.Amount));}Kotlin does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so tagging aprojection has no effect yet.Java does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so tagging aprojection has no effect yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.Multiple Tags (Single Attribute)
Section titled “Multiple Tags (Single Attribute)”Use the params feature to specify multiple tags in a single attribute:
using Cratis.Chronicle;using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record TaggingSaleRecorded(string ProductId, decimal Amount);
public record TaggingSalesReport(string ProductId, decimal TotalSales);
[Tag("Analytics", "Reporting", "Dashboard")]public class TaggingSalesReportProjection : IProjectionFor<TaggingSalesReport>{ public void Define(IProjectionBuilderFor<TaggingSalesReport> builder) => builder .From<TaggingSaleRecorded>(_ => _ .Set(m => m.ProductId).To(e => e.ProductId) .Add(m => m.TotalSales).With(e => e.Amount));}Kotlin does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so a `@Tag` withmultiple values on a projection has no effect yet.Java does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so a `@Tag` withmultiple values on a projection has no effect yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.Multiple Tags (Multiple Attributes)
Section titled “Multiple Tags (Multiple Attributes)”Apply multiple [Tag] attributes:
using Cratis.Chronicle;using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record TaggingAuditEntryRecorded(string EntryId, string Category);
public record TaggingComplianceReport(string EntryId, string Category);
[Tag("Analytics")][Tag("Compliance")][Tag("Auditing")]public class TaggingComplianceReportProjection : IProjectionFor<TaggingComplianceReport>{ public void Define(IProjectionBuilderFor<TaggingComplianceReport> builder) => builder .From<TaggingAuditEntryRecorded>(_ => _ .Set(m => m.EntryId).To(e => e.EntryId) .Set(m => m.Category).To(e => e.Category));}Kotlin does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so repeated `@Tag`attributes on a projection have no effect yet.Java does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so repeated `@Tag`attributes on a projection have no effect yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.Mixed Approach
Section titled “Mixed Approach”Combine both approaches:
using Cratis.Chronicle;using Cratis.Chronicle.Events;using Cratis.Chronicle.Projections;
[EventType]public record TaggingKpiRecorded(string Kpi, decimal Value);
public record TaggingExecutiveDashboard(string Kpi, decimal Value);
[Tag("Analytics", "Reporting")][Tag("Executive")]public class TaggingExecutiveDashboardProjection : IProjectionFor<TaggingExecutiveDashboard>{ public void Define(IProjectionBuilderFor<TaggingExecutiveDashboard> builder) => builder .From<TaggingKpiRecorded>(_ => _ .Set(m => m.Kpi).To(e => e.Kpi) .Set(m => m.Value).To(e => e.Value));}Kotlin does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither the declarative nor the model-bound projectionbuilder in `ProjectionsService` ever reads a `Tag` annotation off a projection class, so tagging aprojection (fluent or model-bound) has no effect yet.Java does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither the declarative nor the model-bound projectionbuilder in `ProjectionsService` ever reads a `Tag` annotation off a projection class, so tagging aprojection (fluent or model-bound) has no effect yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.Model-Bound Projections
Section titled “Model-Bound Projections”Tags also work with model-bound projections:
using Cratis.Chronicle;using Cratis.Chronicle.Events;using Cratis.Chronicle.Keys;using Cratis.Chronicle.Projections.ModelBound;
[EventType]public record TaggingProductRegistered(Guid ProductId, string Name, int QuantityInStock, decimal UnitPrice);
[Tag("Inventory", "Operations")][FromEvent<TaggingProductRegistered>(key: nameof(TaggingProductRegistered.ProductId))]public record TaggingProductInventory( [Key] Guid ProductId, string Name, int QuantityInStock, decimal UnitPrice);Kotlin does not support this workflow yet.`@Tag` only labels a reactor or reducer — `ProjectionsService.buildModelBoundDefinition` never reads a`Tag` annotation off the read model class, so tagging a model-bound projection has no effect yet.Java does not support this workflow yet.`@Tag` only labels a reactor or reducer — `ProjectionsService.buildModelBoundDefinition` never reads a`Tag` annotation off the read model class, so tagging a model-bound projection has no effect yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.Best Practices
Section titled “Best Practices”- Use meaningful names: Choose tag names that clearly describe the purpose or domain
- Be consistent: Establish tag naming conventions across your organization
- Don’t over-tag: Apply only relevant tags; too many can reduce their usefulness
- Group related projections: Use tags to group projections that serve similar purposes
Common Tag Examples
Section titled “Common Tag Examples”Here are some common patterns for tagging projections:
using Cratis.Chronicle;using Cratis.Chronicle.Projections;
public record TaggingCategoryExamples(Guid Id);
// By domain[Tag("Sales", "Inventory", "Customer")]// By purpose[Tag("Analytics", "Reporting", "Dashboard", "Search")]// By stakeholder[Tag("Executive", "Operations", "Finance")]// By consistency model[Tag("Immediate", "Eventual")]// By data type[Tag("Aggregates", "Lists", "Details")]public class TaggingCategoryExamplesProjection : IProjectionFor<TaggingCategoryExamples>{ public void Define(IProjectionBuilderFor<TaggingCategoryExamples> builder) { }}Kotlin does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so tagging aprojection by domain, purpose, stakeholder, or any other category has no effect yet.Java does not support this workflow yet.`@Tag` only labels a reactor or reducer — neither `ProjectionsService.buildDeclarativeDefinition` nor`buildModelBoundDefinition` ever reads a `Tag` annotation off a projection class, so tagging aprojection by domain, purpose, stakeholder, or any other category has no effect yet.Elixir does not support this workflow yet.TypeScript does not support this workflow yet.Querying by Tag
Section titled “Querying by Tag”Tags stored in the event store definition can be used for:
- Filtering and searching for specific projections
- Organizing projections in administrative interfaces
- Generating documentation
- Managing projection deployments by tag
- Grouping projections for rebuild operations
Note: The specific querying capabilities depend on your Chronicle setup and tooling.
See Also
Section titled “See Also”- Tagging - Comprehensive guide to tagging in Chronicle
- Reactors Tagging - Tagging reactors
- Reducers Tagging - Tagging reducers