### Complete Development AppHost Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/aspire.md A simplified Aspire AppHost Program.cs for development, omitting MongoDB wiring and using the default development Chronicle setup. ```csharp var builder = DistributedApplication.CreateBuilder(args); var chronicle = builder.AddCratisChronicle(); builder.AddProject("api") .WithReference(chronicle); builder.Build().Run(); ``` -------------------------------- ### Complete Production AppHost Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/aspire.md A typical Aspire AppHost Program.cs for a production setup, including an external MongoDB resource and referencing the Chronicle resource. ```csharp var builder = DistributedApplication.CreateBuilder(args); var mongo = builder.AddConnectionString("chronicle-mongo"); var chronicle = builder.AddCratisChronicle(chronicle => chronicle.WithMongoDB(mongo)); builder.AddProject("api") .WithReference(chronicle); builder.Build().Run(); ``` -------------------------------- ### Initialize and Render Benchmarks Source: https://github.com/cratis/chronicle/blob/main/Documentation/benchmarks/index.html Initializes the benchmark rendering process by getting the main element and then iterating through data sets to render each benchmark set. Starts the overall rendering. ```javascript const main = document.getElementById('main'); for (const {name, dataSet} of dataSets) { renderBenchSet(name, dataSet, main); } } renderAllChars(init()); // Start })(); ``` -------------------------------- ### ASP.NET Core Web App Setup with Chronicle Source: https://github.com/cratis/chronicle/blob/main/Documentation/namespaces/aspnetcore.md Example of setting up an ASP.NET Core web application with Cratis Chronicle, including configuring the event store and a custom HTTP header for namespace resolution. It also demonstrates appending an event to the event log. ```csharp using Cratis.Chronicle; using Cratis.Chronicle.EventSequences; var builder = WebApplication.CreateBuilder(args); builder.AddCratisChronicle(options => { options.EventStore = "production-store"; options.WithHttpHeaderNamespaceResolver("x-tenant-id"); }); var app = builder.Build(); app.MapPost("/api/cart/{cartId}/items", async (string cartId, IEventLog eventLog) => { var itemAdded = new ItemAddedToCart(ProductId: "product-123", Quantity: 1); await eventLog.Append(cartId, itemAdded); return Results.Ok(); }); app.Run(); record ItemAddedToCart(string ProductId, int Quantity); ``` -------------------------------- ### Simple Key Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/keys.md A basic example demonstrating a simple key declaration using a single event property. ```pdl from ProductCreated key productId Name = name Price = price ``` -------------------------------- ### Example ProtoGenerator Execution Source: https://github.com/cratis/chronicle/blob/main/Documentation/contributing/clients/protobuf-extraction.md An example command demonstrating how to run the ProtoGenerator tool with specific paths for the contracts DLL and output directory. ```bash dotnet run --project Source/Tools/ProtoGenerator/ProtoGenerator.csproj -- \ Source/Kernel/Contracts/bin/Release/net10.0/Cratis.Chronicle.Contracts.dll \ Source/Kernel/Protobuf ``` -------------------------------- ### Product with Optional Promotion Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/nested.md A projection example for a 'Product' with a nested 'promotion' object, populated from 'PromotionApplied' and cleared by 'PromotionRemoved'. ```pdl projection Product => ProductReadModel from ProductListed Name = name BasePrice = price nested promotion from PromotionApplied Label = promotionName DiscountPercent = discount ValidUntil = expiresAt clear with PromotionRemoved ``` -------------------------------- ### Install dotnet counters tool Source: https://github.com/cratis/chronicle/blob/main/Source/Kernel/Server/README.md Installs the global dotnet counters tool for monitoring .NET applications. Ensure you have the .NET SDK installed. ```shell dotnet tool install --global dotnet-counters ``` -------------------------------- ### API Key Authentication Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/connection-strings/server.md Example of a connection string using API key authentication. ```text ?apiKey=your-api-key ``` -------------------------------- ### Complete Example: Global Store Metrics with ConstantKey Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/model-bound/constant-key.md This example demonstrates how all events, regardless of user or product, can be aggregated into a single `StoreMetrics` document using `ConstantKey` on multiple counter attributes. This provides a unified view of store-wide activity. ```csharp using Cratis.Chronicle.Events; using Cratis.Chronicle.Projections.ModelBound; // Events [EventType] public record ProductPurchased(string ProductId, decimal Amount); [EventType] public record ProductReturned(string ProductId, decimal Amount); [EventType] public record PageViewed(string PageUrl); // Global read model public record StoreMetrics( [Count(ConstantKey = "store")] int TotalPurchases, [Count(ConstantKey = "store")] int TotalReturns, [Increment(ConstantKey = "store")] [Decrement(ConstantKey = "store")] int NetTransactions, [Count(ConstantKey = "store")] int TotalPageViews); ``` -------------------------------- ### Example Chronicle Configuration Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/configuration-precedence.md This JSON defines example configuration values for port and management port. It serves as a baseline that can be overridden by environment variables. ```json { "port": 35000, "managementPort": 8080 } ``` -------------------------------- ### Complex Example with Key and Counter Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/from-event.md A comprehensive example mapping several properties from 'OrderPlaced', specifying 'orderId' as the key, and using a counter for 'TotalOrders'. ```pdl from OrderPlaced key orderId CustomerId = customerId OrderNumber = orderNumber Total = total Status = "Pending" PlacedAt = $eventContext.occurred increment TotalOrders ``` -------------------------------- ### Slice with Optional Command Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/nested.md A complete projection example for a 'Slice' with an optional 'command' property, using automap and multiple from events. ```pdl projection Slice => SliceReadModel from SliceCreated Name = name nested command automap from CommandSetForSlice from CommandRenamed Name = newName clear with CommandClearedForSlice ``` -------------------------------- ### Complete Model-Bound Projection Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/model-bound/counters.md This example demonstrates a comprehensive model-bound projection tracking user activity, including login/logout counts, active sessions, transaction counts, and net spending. ```csharp using Cratis.Chronicle.Events; using Cratis.Chronicle.Keys; using Cratis.Chronicle.Projections.ModelBound; // Events [EventType] public record UserLoggedIn(DateTimeOffset Timestamp); [EventType] public record UserLoggedOut(DateTimeOffset Timestamp); [EventType] public record PurchaseMade(decimal Amount); [EventType] public record RefundIssued(decimal Amount); // Read Model public record UserActivity( [Key] Guid UserId, // Track login/logout counts [Count] int TotalLogins, [Count] int TotalLogouts, // Track active sessions [Increment] [Decrement] int ActiveSessions, // Track transaction counts [Count] int PurchaseCount, [Count] int RefundCount, // Track transaction values [AddFrom(nameof(PurchaseMade.Amount))] [SubtractFrom(nameof(RefundIssued.Amount))] decimal NetSpent); ``` -------------------------------- ### Common Tag Examples Source: https://github.com/cratis/chronicle/blob/main/Documentation/concepts/tagging-reactors.md Illustrates common patterns for applying tags to reactors, categorized by integration type, domain, communication channel, purpose, and stakeholder. These examples serve as a guide for consistent and effective reactor organization. ```csharp // By integration type [Tag("Notifications")] [Tag("ExternalAPI")] [Tag("MessageQueue")] [Tag("FileSystem")] // By domain [Tag("Sales")] [Tag("Inventory")] [Tag("Customer")] [Tag("Shipping")] // By communication channel [Tag("Email")] [Tag("SMS")] [Tag("Push")] [Tag("Webhook")] // By purpose [Tag("Integration")] [Tag("Alerting")] [Tag("Monitoring")] [Tag("Automation")] // By stakeholder [Tag("Customer")] [Tag("Operations")] [Tag("Finance")] [Tag("Support")] ``` -------------------------------- ### Get or Create Default Account Info Source: https://github.com/cratis/chronicle/blob/main/Documentation/read-models/getting-single-instance.md Handles cases where a read model might not have any events yet. If GetInstanceById returns null (or default values), this example shows how to return a pre-defined default or initial state. ```csharp public async Task GetOrCreateDefaultAccount(Guid accountId) { var account = await _eventStore.ReadModels.GetInstanceById(accountId); // For new accounts with no events, you'll get default values if (account.Name == string.Empty) { // This is a new account that hasn't been initialized return new AccountInfo(accountId, "New Account", 0m); } return account; } ``` -------------------------------- ### Example Chronicle Configuration JSON Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/docker.md This is an example of the JSON structure used for Chronicle configuration. ```json { "managementPort": 8080, "port": 35000, "storage": { "type": "MongoDB", "connectionDetails": "mongodb://mongo:27017" } } ``` -------------------------------- ### Standalone ChronicleClient Bootstrap Source: https://context7.com/cratis/chronicle/llms.txt Initializes a Cratis Chronicle client for non-web hosts like console or worker services. This example demonstrates creating a client, getting an event store, discovering and registering artifacts, and appending an event. ```csharp // Program.cs — Console or Worker Service using Cratis.Chronicle; using var client = new ChronicleClient( ChronicleOptions.FromUrl("http://localhost:35000") .WithCamelCaseNamingPolicy()); var eventStore = await client.GetEventStore("Quickstart"); // Discover and register all artifacts from the current assembly await eventStore.DiscoverAndRegisterAll(); // Use the event log var result = await eventStore.EventLog.Append( Guid.NewGuid(), new UserOnboarded("Jane Doe", "jane@example.com")); Console.WriteLine(result.IsSuccess ? "Event appended" : $"Failed: {string.Join(", ", result.Errors)}"); ``` -------------------------------- ### Simple AutoMap Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/declarative/auto-map.md A basic example demonstrating AutoMap for simple projections where property names and types match between events and the read model. ```csharp public class ProductProjection : IProjectionFor { public void Define(IProjectionBuilderFor builder) => builder .AutoMap() .From() .From(); } ``` -------------------------------- ### Order with Line Items Projection Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/children.md An example projection for 'OrderReadModel' with 'items' children, managing 'LineItemAdded', 'LineItemQuantityChanged', and 'LineItemRemoved' events. ```pdl projection Order => OrderReadModel from OrderPlaced OrderNumber = orderNumber CustomerId = customerId Total = 0 children items id lineNumber from LineItemAdded key lineNumber parent orderId ProductId = productId Quantity = quantity UnitPrice = price LineTotal = total from LineItemQuantityChanged key lineNumber parent orderId Quantity = quantity LineTotal = total remove with LineItemRemoved key lineNumber parent orderId ``` -------------------------------- ### Children with Keys Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/keys.md Example of defining child projections with a key specified on the 'from' statement. ```pdl children orderLines identified by lineNumber from LineItemAdded key lineNumber parent orderId Product = productName Quantity = quantity Price = price ``` -------------------------------- ### Complete Example with Nested Joins Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/model-bound/joins.md A comprehensive example showcasing joins at multiple levels, including nested joins within child collections and multiple joins on the same read model property. ```csharp // Events [EventType] public record OrderPlaced(Guid CustomerId, DateTimeOffset PlacedAt); [EventType] public record CustomerRegistered(string Name, string Email); [EventType] public record CustomerProfileUpdated(string PhoneNumber); [EventType] public record LineItemAdded(Guid ProductId, int Quantity); [EventType] public record ProductCreated(string Name, decimal Price); [EventType] public record ProductPriceChanged(decimal NewPrice); // Read Models public record OrderDetails( [Key] Guid OrderId, [SetFrom] DateTimeOffset PlacedAt, // Join customer information [Join( on: nameof(CustomerId), eventPropertyName: nameof(CustomerRegistered.Name))] string CustomerName, [Join( on: nameof(CustomerId), eventPropertyName: nameof(CustomerRegistered.Email))] string CustomerEmail, [Join( on: nameof(CustomerId), eventPropertyName: nameof(CustomerProfileUpdated.PhoneNumber))] string CustomerPhone, [ChildrenFrom(key: nameof(LineItemAdded.ProductId))] IEnumerable Items); public record LineItemDetails( [Key] Guid ProductId, [SetFrom] int Quantity, // Join product information [Join(eventPropertyName: nameof(ProductCreated.Name))] string ProductName, [Join(eventPropertyName: nameof(ProductCreated.Price))] [Join(eventPropertyName: nameof(ProductPriceChanged.NewPrice))] decimal Price); ``` -------------------------------- ### Install Chronicle Contracts Package Source: https://github.com/cratis/chronicle/blob/main/Documentation/contributing/clients/typescript-grpc-package.md Install the @cratis/chronicle.contracts npm package using either npm or yarn. ```bash npm install @cratis/chronicle.contracts # or yarn add @cratis/chronicle.contracts ``` -------------------------------- ### Basic Projection Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/grammar.md An example of a simple projection declaration, specifying the event it reads from and a basic mapping. ```pdl projection User => UserReadModel from UserCreated Name = name ``` -------------------------------- ### Complete Model-Bound Children Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/model-bound/children.md A comprehensive example demonstrating the full attribute support for defining child collections, including initial addition, updates, and removal. ```csharp using Cratis.Chronicle.Events; using Cratis.Chronicle.Keys; using Cratis.Chronicle.Projections.ModelBound; // Events [EventType] public record OrderCreated(string CustomerName); [EventType] public record LineItemAdded( Guid ItemId, string ProductName, int InitialQuantity, decimal UnitPrice); [EventType] public record QuantityAdjusted(Guid ItemId, int NewQuantity); [EventType] public record LineItemRemoved(Guid ItemId); // Read Models public record Order( [Key] Guid Id, [SetFrom(nameof(OrderCreated.CustomerName))] string Customer, [ChildrenFrom(key: nameof(LineItemAdded.ItemId))] [RemovedWith(key: nameof(LineItemRemoved.ItemId))] IEnumerable Lines); public record OrderLine( [Key] Guid Id, [SetFrom(nameof(LineItemAdded.ProductName))] [SetFrom(nameof(LineItemAdded.InitialQuantity))] [SetFrom(nameof(QuantityAdjusted.NewQuantity))] int Quantity, [SetFrom(nameof(LineItemAdded.UnitPrice))] decimal UnitPrice); ``` -------------------------------- ### Example Event and Read Model for AutoMap Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/auto-map.md Illustrates an example event record and a read model class. With default AutoMap, properties with matching names and compatible types (Name, Email, Age) are automatically copied. ```csharp public record UserRegistered(string Name, string Email, int Age); public class UserReadModel { public string Name { get; set; } public string Email { get; set; } public int Age { get; set; } public bool IsActive { get; set; } } ``` -------------------------------- ### Simple Property Mapping Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/from-event.md A straightforward example of mapping properties from an 'OrderPlaced' event to a read model, including a static string value for 'Status'. ```pdl from OrderPlaced CustomerId = customerId Total = total Status = "Pending" ``` -------------------------------- ### Default AutoMap Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/auto-map.md This example demonstrates the default AutoMap behavior where properties like Name, Price, and Description are automatically mapped if they exist in both the event and read model. An explicit mapping for CreatedAt is also shown. ```pdl projection Product => ProductReadModel from ProductCreated # Name, Price, Description auto-mapped if they exist CreatedAt = $eventContext.occurred ``` -------------------------------- ### Increment Example: User Logins Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/counters.md This example shows how to use the `increment` operation to count the number of times a `UserLoggedIn` event occurs, increasing the `LoginCount` property. ```pdl from UserLoggedIn increment LoginCount LastLogin = $eventContext.occurred ``` -------------------------------- ### Example JSON Configuration Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/environment-variables.md Illustrates the structure of a typical Cratis Chronicle configuration in JSON format. ```json { "managementPort": 8080, "port": 35000, "features": { "api": true, "workbench": true }, "storage": { "type": "MongoDB", "connectionDetails": "mongodb://localhost:27017" } } ``` -------------------------------- ### Configure Storage Provider Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/environment-variables.md Set the storage type and connection details. For example, specify 'MongoDB' and provide the connection string. ```bash # Storage type (e.g., "MongoDB") Cratis__Chronicle__Storage__Type=MongoDB # MongoDB connection string Cratis__Chronicle__Storage__ConnectionDetails=mongodb://localhost:27017 ``` -------------------------------- ### Install Cratis.Chronicle.Testing NuGet Package Source: https://github.com/cratis/chronicle/blob/main/Documentation/testing/read-models/scenario.md Add the Cratis.Chronicle.Testing NuGet package to your project to use the ReadModelScenario utility. ```bash dotnet add package Cratis.Chronicle.Testing ``` -------------------------------- ### Subscription with Plan Removal Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/removal.md This example demonstrates a 'Subscription' projection that can be removed either directly via 'SubscriptionCancelled' or indirectly via 'PlanDiscontinued' on a joined stream. ```pdl projection Subscription => SubscriptionReadModel from SubscriptionCreated UserId = userId PlanId = planId StartDate = startDate join SubscriptionPlan on PlanId events PlanCreated, PlanUpdated PlanName = name Price = price remove with SubscriptionCancelled remove via join on PlanDiscontinued ``` -------------------------------- ### Group Membership Projection Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/children.md A complete projection example for 'GroupReadModel' including 'members' children, handling 'UserAddedToGroup', 'UserRoleChanged', and 'UserRemovedFromGroup' events with automapping and removal. ```pdl projection Group => GroupReadModel from GroupCreated Name = name Description = description children members id userId automap from UserAddedToGroup key userId parent groupId AddedAt = $eventContext.occurred from UserRoleChanged key userId parent groupId Role = role remove with UserRemovedFromGroup key userId parent groupId ``` -------------------------------- ### Order Processing Timeline Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/read-models/getting-snapshots.md Illustrates how events with different correlation IDs contribute to distinct snapshots of an order's state. ```csharp // Initial order creation (Correlation ID: A) await _eventStore.EventLog.Append(orderId, new OrderCreated(orderId, customerId)); await _eventStore.EventLog.Append(orderId, new OrderItemAdded(productId, quantity)); // Payment processing (Correlation ID: B) await _eventStore.EventLog.Append(orderId, new PaymentReceived(amount)); await _eventStore.EventLog.Append(orderId, new OrderConfirmed()); // Shipping (Correlation ID: C) await _eventStore.EventLog.Append(orderId, new OrderShipped(trackingNumber)); // Retrieve all snapshots var snapshots = await _eventStore.ReadModels.GetSnapshotsById(orderId); // You'll get 3 snapshots: // 1. After creation (events from correlation A) // 2. After payment (events from correlation B) // 3. After shipping (events from correlation C) ``` -------------------------------- ### Event Source as Key Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/keys.md Explicitly using the event source ID as the key for a projection. ```pdl from AccountCreated key $eventSourceId AccountNumber = accountNumber Balance = 0.0 ``` -------------------------------- ### Simple Join Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/joins.md Illustrates a basic join to enrich an 'Order' projection with customer details from 'CustomerCreated' or 'CustomerUpdated' events. ```pdl projection Order => OrderReadModel from OrderPlaced OrderNumber = orderNumber CustomerId = customerId join Customer on CustomerId with CustomerCreated CustomerName = name CustomerEmail = email with CustomerUpdated CustomerName = name CustomerEmail = email ``` -------------------------------- ### Common Tag Examples by Domain, Purpose, Stakeholder, and Data Type Source: https://github.com/cratis/chronicle/blob/main/Documentation/reducers/tagging-reducers.md Illustrates common patterns for applying tags to reducers based on their domain, purpose, stakeholder, or the type of data they process. These examples serve as a guide for consistent tagging strategies. ```csharp // By domain [Tag("Sales")] [Tag("Inventory")] [Tag("Customer")] // By purpose [Tag("Analytics")] [Tag("Reporting")] [Tag("Dashboard")] [Tag("Auditing")] // By stakeholder [Tag("Executive")] [Tag("Operations")] [Tag("Finance")] // By data type [Tag("Aggregates")] [Tag("Summaries")] [Tag("Metrics")] ``` -------------------------------- ### Full Example: Register Author and Add Book Source: https://github.com/cratis/chronicle/blob/main/Documentation/testing/events/event-sequence-assertions.md A complete test scenario that pre-seeds state, appends two events, and then verifies the tail sequence number and individual event content in order. ```csharp public class when_registering_an_author_and_adding_a_book { readonly EventScenario _scenario = new(); [Fact] public async Task should_append_both_events_in_order() { var authorId = AuthorId.New(); await _scenario.Given .ForEventSource(authorId) .Events(new LibraryCreated("Main Library")); await _scenario.EventLog.Append(authorId, new AuthorRegistered("Jane Smith")); await _scenario.EventLog.Append(authorId, new BookAdded("Clean Code")); // Tail includes the seeded event (0) plus the two appended events (1, 2) await _scenario.EventLog.ShouldHaveTailSequenceNumber(2); await _scenario.EventLog.ShouldHaveAppendedEvent(1, author => author.Name.ShouldEqual("Jane Smith")); await _scenario.EventLog.ShouldHaveAppendedEvent(2, book => book.Title.ShouldEqual("Clean Code")); } } ``` -------------------------------- ### Simple Nested Object Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/nested.md Illustrates a basic projection with a nested 'command' object populated from 'CommandSetForSlice' and cleared by 'CommandClearedForSlice'. ```pdl projection Slice => SliceReadModel from SliceCreated Name = name nested command from CommandSetForSlice Name = commandName Schema = schema clear with CommandClearedForSlice ``` -------------------------------- ### Get All Reducer-Based Read Model Instances Source: https://github.com/cratis/chronicle/blob/main/Documentation/read-models/getting-collection-instances.md Retrieve all instances of a read model defined using reducers. This example shows how to fetch all shopping cart instances. ```csharp public record ShoppingCart(Guid Id, List Items, decimal Total); public class ShoppingCartReducer : IReducerFor { public ReducerId Identifier => "ShoppingCart"; public ShoppingCart Initial => new(Guid.Empty, [], 0m); public ShoppingCart Reduce(ShoppingCart current, object @event) => @event switch { CartCreated e => current with { Id = e.CartId }, ItemAdded e => current with { Items = [..current.Items, new CartItem(e.ProductId, e.Quantity, e.Price)], Total = current.Total + (e.Quantity * e.Price) }, _ => current }; } // Retrieve all shopping carts public async Task> GetAllActiveCarts() { var carts = await _eventStore.ReadModels.GetInstances(); return carts.Where(c => c.Items.Any()).ToList(); } ``` -------------------------------- ### ASP.NET Core Bootstrap with Cratis Chronicle Source: https://context7.com/cratis/chronicle/llms.txt Integrates Cratis Chronicle into an ASP.NET Core application. This method registers Chronicle services, discovers artifacts, and starts the background connection to the Kernel. It also shows an example of appending an event using `IEventLog`. ```csharp // Program.cs — ASP.NET Core using Cratis.Chronicle; var builder = WebApplication.CreateBuilder(args) .AddCratisChronicle(options => { options.EventStore = "MyApp"; options.ConnectionString = "chronicle://localhost:35000"; options.ProgramIdentifier = "my-app"; }); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); var app = builder.Build(); app.UseCratisChronicle(); // Example minimal API endpoint that appends an event app.MapPost("/api/orders/{orderId}/place", async ( [FromServices] IEventLog eventLog, [FromRoute] Guid orderId, [FromBody] PlaceOrderRequest req) => { var result = await eventLog.Append(orderId, new OrderPlaced(req.CustomerId, req.Total)); return result.IsSuccess ? Results.Ok() : Results.Conflict(result.Errors); }); app.Run(); ``` -------------------------------- ### Create .NET Console Project Source: https://github.com/cratis/chronicle/blob/main/Documentation/get-started/console.md Use the dotnet CLI to create a new console application. ```shell dotnet new console ``` -------------------------------- ### Disable TLS Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/connection-strings/server.md Example of a connection string with TLS disabled. ```text ?disableTls=true ``` -------------------------------- ### Create EventSourceId from Guid, String, or New Source: https://github.com/cratis/chronicle/blob/main/Documentation/events/event-source-id.md Demonstrates creating an EventSourceId from a Guid, a string, or by generating a new random identifier. Use Guid for aggregate-style identifiers and strings for natural keys. ```csharp using Cratis.Chronicle.Events; // From a Guid — common for aggregate-style identifiers EventSourceId id = Guid.NewGuid(); // From a string — useful for natural keys EventSourceId id = "order-42"; // Generate a new random identifier EventSourceId id = EventSourceId.New(); ``` -------------------------------- ### Create ASP.NET Core Project Source: https://github.com/cratis/chronicle/blob/main/Documentation/get-started/aspnetcore.md Use the dotnet CLI to create a new ASP.NET Core web project. ```shell dotnet new web ``` -------------------------------- ### Subtract Operation Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/arithmetic.md Example of subtracting an `amount` from a `WithdrawalMade` event from the `Balance` property. ```pdl from WithdrawalMade subtract Balance by amount LastWithdrawal = $eventContext.occurred ``` -------------------------------- ### Add Operation Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/arithmetic.md Example of adding an `amount` from a `PaymentReceived` event to the `Balance` property. ```pdl from PaymentReceived add Balance by amount LastPayment = $eventContext.occurred ``` -------------------------------- ### Account Creation with Literals and Context Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/property-mapping.md Map account creation data, setting initial literal values for balance and type, and capturing the opening time and source ID from the event context. ```pdl from AccountCreated AccountNumber = accountNumber Balance = 0.0 IsActive = true AccountType = "Standard" OpenedAt = $eventContext.occurred OpenedBy = $eventContext.eventSourceId ``` -------------------------------- ### Example with Custom Key for Order Projection Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/model-bound/convention-based.md Illustrates using a custom key from an event to map to a read model, specifically when the event's property identifies the target read model instance. ```csharp [EventType] public record OrderLineItemAdded( Guid OrderId, // Key for the Order read model Guid LineItemId, // Key for individual line items string ProductName, int Quantity, decimal Price); // Order projection using OrderId as key [FromEvent(key: nameof(OrderLineItemAdded.OrderId))] public record Order( [Key] Guid Id, // Properties auto-mapped from OrderLineItemAdded decimal TotalAmount); ``` -------------------------------- ### Configure and Initialize Chronicle Client Source: https://github.com/cratis/chronicle/blob/main/Documentation/get-started/console.md Configure the Chronicle client with the server URL and naming policy, then get an event store. This snippet relies on automatic discovery of artifacts. ```csharp using Cratis.Chronicle; // Explicitly use the Chronicle Options to set the naming policy to camelCase for the projection/reducer sinks using var client = new ChronicleClient(ChronicleOptions.FromUrl("http://localhost:35000").WithCamelCaseNamingPolicy()); var eventStore = await client.GetEventStore("Quickstart"); ``` -------------------------------- ### Run All Tests in a Suite (Release Build) Source: https://github.com/cratis/chronicle/blob/main/Documentation/contributing/integration-tests.md Execute all integration tests within a specific suite using the provided convenience script for a release build. ```bash cd Integration/DotNET.InProcess ./run.sh ``` -------------------------------- ### Start Docker Compose Services Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/open-telemetry.md Command to start the services defined in the docker-compose.yml file in detached mode. ```bash docker compose up -d ``` -------------------------------- ### Client Initialization from Connection String Source: https://github.com/cratis/chronicle/blob/main/Documentation/connection-strings/dotnet-client.md Initialize ChronicleOptions and ChronicleClient by providing a specific connection string. This is recommended for production environments. ```csharp var options = ChronicleOptions.FromConnectionString("chronicle://localhost:35000"); var client = new ChronicleClient(options); ``` -------------------------------- ### Quick Start Chronicle Connection Source: https://github.com/cratis/chronicle/blob/main/Source/Clients/TypeScript/README.md Create and connect to a Chronicle instance using a connection string, then interact with services like event stores. ```typescript import { ChronicleConnection } from '@cratis/chronicle.contracts'; // Create a connection using a connection string const connection = new ChronicleConnection({ connectionString: 'chronicle://localhost:35000' }); // Connect to Chronicle await connection.connect(); // Use the services with full type safety, IDE completion, and async/await const eventStores = await connection.eventStores.getEventStores({}); console.log('Event stores:', eventStores.items); // Clean up connection.dispose(); ``` -------------------------------- ### Type Compatibility Examples Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/expressions.md Ensure expressions are compatible with the target property type. Invalid examples show type mismatches. ```csharp public class UserReadModel { public string Name { get; set; } // Requires string public int LoginCount { get; set; } // Requires number public bool IsActive { get; set; } // Requires boolean public DateTime CreatedAt { get; set; } // Requires timestamp } ``` ```pdl Name = name # string LoginCount = 0 # number IsActive = true # boolean CreatedAt = $eventContext.occurred # timestamp ``` ```pdl Name = 123 # number to string (invalid) LoginCount = "five" # string to number (invalid) IsActive = "yes" # string to boolean (invalid) ``` -------------------------------- ### Multiple Events with Compact Syntax Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/from-event.md Demonstrates how to specify multiple event types on a single line when they share identical mappings and configurations, using 'automap' for property copying. ```pdl projection TransportRoute => TransportRouteReadModel automap from HubRouteAdded key id, WarehouseRouteAdded key id ``` -------------------------------- ### Complete Model-Bound Projection Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/model-bound/basic-mapping.md Demonstrates the usage of SetFrom, AddFrom, and SubtractFrom attributes for mapping event properties to a read model. ```csharp using Cratis.Chronicle.Events; using Cratis.Chronicle.Keys; using Cratis.Chronicle.Projections.ModelBound; // Events [EventType] public record AccountOpened(string AccountName, decimal InitialBalance); [EventType] public record DepositMade(decimal Amount); [EventType] public record WithdrawalMade(decimal Amount); [EventType] public record AccountRenamed(string NewName); // Read Model public record BankAccount( [Key] Guid Id, [SetFrom(nameof(AccountOpened.AccountName))] [SetFrom(nameof(AccountRenamed.NewName))] string Name, [SetFrom(nameof(AccountOpened.InitialBalance))] [AddFrom(nameof(DepositMade.Amount))] [SubtractFrom(nameof(WithdrawalMade.Amount))] decimal Balance); ``` -------------------------------- ### Run All Tests with dotnet test (Release) Source: https://github.com/cratis/chronicle/blob/main/Documentation/contributing/integration-tests.md Invoke all integration tests for a specific project using the `dotnet test` command with release configuration and normal verbosity. ```bash dotnet test Integration/DotNET.InProcess/DotNET.InProcess.csproj \ --logger "console;verbosity=normal" \ --configuration Release \ --framework net10.0 ``` -------------------------------- ### Decrement Example: Stock Levels Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/counters.md This example illustrates using the `decrement` operation to reduce the `RemainingStock` property when an `ItemConsumed` event occurs. ```pdl from ItemConsumed decrement RemainingStock LastConsumed = $eventContext.occurred ``` -------------------------------- ### Configure OTLP Endpoint and Headers for Cloud Backends Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/open-telemetry.md Set the OTLP endpoint and any required authentication headers for cloud observability backends like Datadog, Honeycomb, Grafana Cloud, or Azure Monitor. ```bash OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.example.com OTEL_EXPORTER_OTLP_HEADERS=x-api-key=your-api-key ``` -------------------------------- ### Configure Clients via Environment Variables Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/client-bootstrap.md Set environment variables to configure clients, following a specific naming convention for nested properties. ```bash Cratis__Chronicle__Clients__0__ClientId=my-service Cratis__Chronicle__Clients__0__ClientSecret=a-strong-secret-value Cratis__Chronicle__Clients__1__ClientId=another-service Cratis__Chronicle__Clients__1__ClientSecret=another-secret-value ``` -------------------------------- ### Using Individual Services Directly Source: https://github.com/cratis/chronicle/blob/main/Source/Clients/TypeScript/README.md Demonstrates how to import and use specific Chronicle services, such as EventStoresClient, directly. ```APIDOC ## Using Individual Services Directly ### Description Import and instantiate individual service clients to interact with specific Chronicle functionalities. ### Method Import the desired client (e.g., `EventStoresClient`) and instantiate it with the server address and credentials. ### Endpoint N/A (Client-side instantiation) ### Parameters #### Client Instantiation - **serverAddress** (string) - Required - The address of the Chronicle server (e.g., 'localhost:35000'). - **credentials** (grpc.Credentials) - Required - gRPC credentials (e.g., `grpc.credentials.createInsecure()`). ### Request Example ```typescript import { EventStoresClient } from '@cratis/chronicle.contracts'; import * as grpc from '@grpc/grpc-js'; const client = new EventStoresClient( 'localhost:35000', grpc.credentials.createInsecure() ); const response = await client.GetEventStores({}); console.log('Event stores:', response.items); ``` ### Response #### Success Response (GetEventStores) - **items** (Array) - A list of event stores. #### Response Example (GetEventStores) ```json { "items": [ // ... event store objects ] } ``` ``` -------------------------------- ### Composite Key Example with Type Definition Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/keys.md An example of a composite key defined within a projection, referencing a specific type for the key structure. ```pdl projection OrderLine => OrderLineReadModel from LineItemAdded key OrderLineKey { OrderId = orderId ProductId = productId } Quantity = quantity UnitPrice = unitPrice LineTotal = total ``` -------------------------------- ### Example Chronicle Server Configuration Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/configuration-file.md This JSON object demonstrates a typical configuration for the Chronicle Server. It covers settings for network ports, feature toggles, storage details, observer behavior, event queues, authentication credentials, and identity provider certificates. Note that environment variables can override these values. ```json { "managementPort": 8080, "port": 35000, "healthCheckEndpoint": "/health", "features": { "api": true, "workbench": true, "changesetStorage": false, "oAuthAuthority": true }, "storage": { "type": "MongoDB", "connectionDetails": "mongodb://localhost:27017" }, "observers": { "subscriberTimeout": 5, "maxRetryAttempts": 10, "backoffDelay": 1, "exponentialBackoffDelayFactor": 2, "maximumBackoffDelay": 600 }, "events": { "queues": 8 }, "authentication": { "authority": null, "defaultAdminUsername": "admin", "defaultAdminPassword": "admin" }, "identityProvider": { "certificate": { "enabled": true, "certificatePath": "/path/to/identity-provider.pfx", "certificatePassword": "your-password" } } } ``` -------------------------------- ### Employee with Optional Active Contract Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/nested.md Example projection for an 'Employee' with a nested 'activeContract' object, demonstrating updates from 'ContractStarted' and 'ContractExtended', and clearing with 'ContractEnded'. ```pdl projection Employee => EmployeeReadModel from EmployeeHired Name = name Department = department nested activeContract from ContractStarted ContractId = contractId StartDate = startDate EndDate = endDate Type = contractType from ContractExtended EndDate = newEndDate clear with ContractEnded ``` -------------------------------- ### Generate Proto Files Locally Source: https://github.com/cratis/chronicle/blob/main/Documentation/contributing/clients/protobuf-extraction.md Steps to build the Contracts assembly and then generate proto files using the ProtoGenerator tool. This involves changing directories and running dotnet commands. ```bash # Build the Contracts assembly cd Source/Kernel/Contracts dotnet build -c Release # Generate proto files cd ../../.. dotnet run --project Source/Tools/ProtoGenerator/ProtoGenerator.csproj -- \ Source/Kernel/Contracts/bin/Release/net10.0/Cratis.Chronicle.Contracts.dll \ Source/Kernel/Protobuf ``` -------------------------------- ### Count Example: Page Views Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/counters.md This example demonstrates how to use the `count` operation to track the number of times a `PageViewed` event occurs, incrementing the `ViewCount` property. ```pdl from PageViewed count ViewCount LastViewedAt = $eventContext.occurred ``` -------------------------------- ### Default Development Client Initialization Source: https://github.com/cratis/chronicle/blob/main/Documentation/connection-strings/dotnet-client.md Use default constructors for ChronicleOptions and ChronicleClient for development environments. These implicitly use the development connection string. ```csharp var options = new ChronicleOptions(); var client = new ChronicleClient(); ``` -------------------------------- ### Install Cratis Chronicle NuGet Packages Source: https://context7.com/cratis/chronicle/llms.txt Installs the necessary Cratis Chronicle NuGet packages for ASP.NET Core or standalone .NET applications. Use the appropriate package based on your application type. ```shell docker compose up -d # Install NuGet packages dotnet add package Cratis.Chronicle.AspNetCore # for ASP.NET Core dotnet add package Cratis.Chronicle # for console / worker ``` -------------------------------- ### Disable AutoMap Entirely Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/auto-map.md This example shows how to disable AutoMap for an entire projection using 'no automap'. All property mappings, including OrderId, CustomerId, and Total, must be explicitly defined. ```pdl projection Order => OrderReadModel no automap from OrderPlaced OrderId = orderId CustomerId = customerId Total = total Status = "Pending" ``` -------------------------------- ### Setup Reactor Test Context Source: https://github.com/cratis/chronicle/blob/main/Documentation/testing/event-append-collection.md Set up the shared context for integration tests involving reactors. This includes specifying event types, reactors, and configuring services. ```csharp namespace MyApp.Integration.for_ShipmentReactor.given; public class a_shipment_reactor_context(ChronicleInProcessFixture fixture) : Specification(fixture) { public EventSourceId EventSourceId; public IEventAppendCollection _appendedEventsCollector; public override IEnumerable EventTypes => [typeof(OrderPlaced), typeof(ShipmentScheduled)]; public override IEnumerable Reactors => [typeof(ShipmentReactor)]; protected override void ConfigureServices(IServiceCollection services) => services.AddSingleton(); void Establish() => EventSourceId = EventSourceId.New(); void Destroy() => _appendedEventsCollector?.Dispose(); } ``` -------------------------------- ### Join with Disabled AutoMap Example Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/auto-map.md This example shows a projection where AutoMap is enabled for the initial 'OrderPlaced' event. However, within the 'Customer' join, AutoMap is disabled ('no automap'), requiring explicit mapping for 'CustomerName'. ```pdl projection Order => OrderReadModel from OrderPlaced # AutoMap enabled join Customer on CustomerId events CustomerCreated, CustomerUpdated no automap CustomerName = name ``` -------------------------------- ### Product Projection with Category Information Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/projection-declaration-language/joins.md Demonstrates a product projection that joins with category events to include category name and description. ```pdl projection Product => ProductReadModel from ProductCreated ProductId = productId Name = name CategoryId = categoryId join Category on CategoryId with CategoryCreated CategoryName = name CategoryDescription = description with CategoryRenamed CategoryName = name CategoryDescription = description ``` -------------------------------- ### Development Client Initialization from Constant Source: https://github.com/cratis/chronicle/blob/main/Documentation/connection-strings/dotnet-client.md Explicitly initialize ChronicleOptions and ChronicleClient using the development connection string constant or a dedicated method. ```csharp var options = ChronicleOptions.FromDevelopmentConnectionString(); var client = new ChronicleClient(ChronicleConnectionString.Development); ``` -------------------------------- ### Generate Certificate with SAN (OpenSSL) Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/local-certificates.md Create a certificate with Subject Alternative Names (SAN) for testing with custom domains, then convert to PFX format. ```bash # Create a configuration file for SAN cat > san.cnf << EOF [req] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [dn] CN = localhost O = Development C = US [req_ext] subjectAltName = @alt_names [alt_names] DNS.1 = localhost DNS.2 = chronicle.local IP.1 = 127.0.0.1 EOF # Generate the certificate with SAN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout chronicle-dev.key -out chronicle-dev.crt \ -config san.cnf -extensions req_ext # Convert to PFX openssl pkcs12 -export -out chronicle-dev.pfx \ -inkey chronicle-dev.key -in chronicle-dev.crt \ -password pass:YourPassword123 ``` -------------------------------- ### Real-world Example: User Groups with RemovedWithJoin in C# Source: https://github.com/cratis/chronicle/blob/main/Documentation/projections/declarative/remove-with-join.md This example demonstrates managing user group memberships. It uses RemovedWith for explicit user departures and RemovedWithJoin for group disbandment, removing memberships from all affected users. ```csharp public class GroupMembershipProjection : IProjectionFor { public void Define(IProjectionBuilderFor builder) => builder .AutoMap() .From(_ => _ .Set(m => m.RegisteredAt).ToEventContextProperty(c => c.Occurred)) .Children(m => m.Memberships, children => children .IdentifiedBy(e => e.GroupId) .AutoMap() .From(_ => _ .UsingParentKey(e => e.UserId) .UsingKey(e => e.GroupId) .Set(m => m.JoinedAt).ToEventContextProperty(c => c.Occurred)) .Join(_ => _ .On(m => m.GroupId)) .RemovedWith(_ => _ .UsingParentKey(e => e.UserId) .UsingKey(e => e.GroupId)) .RemovedWithJoin()); } ``` -------------------------------- ### Bootstrap Admin User with Configuration File Source: https://github.com/cratis/chronicle/blob/main/Documentation/hosting/configuration/authentication.md Pre-configure the initial admin user's credentials directly in the configuration file. This is useful for automated deployments. The plaintext password is immediately hashed and never retained. ```json { "authentication": { "adminUser": { "username": "admin", "password": "a-strong-initial-password", "email": "admin@example.com", "requirePasswordChangeOnFirstLogin": true } } } ``` -------------------------------- ### Full Test Example with Given and Append Source: https://github.com/cratis/chronicle/blob/main/Documentation/testing/events/scenario.md A complete example of an integration test using EventScenario, including pre-seeding state with 'Given' and performing an append operation. Remember to create a new EventScenario instance per test for isolation. ```csharp public class when_adding_a_book_to_an_author { readonly EventScenario _scenario = new(); [Fact] public async Task and_the_author_is_registered() { var authorId = AuthorId.New(); await _scenario.Given .ForEventSource(authorId) .Events(new AuthorRegistered("Jane Smith")); var result = await _scenario.EventLog.Append(authorId, new BookAdded("Clean Code")); result.ShouldBeSuccessful(); } } ```