### EF Core Integration for Data Sources and Repositories Source: https://context7.com/codebeltnet/savvyio/llms.txt Demonstrates setting up Entity Framework Core data sources and repositories within a Dependency Injection container. This includes DbContext configuration and repository registration. ```csharp // 1. Define the EF Core DbContext public class AppDbContext : EfCoreDbContext { public AppDbContext(EfCoreDataSourceOptions options) : base(options) { } public DbSet Accounts { get; set; } } // 2. Register in DI services.AddEfCoreDataSource(o => { o.ContextConfigurator = b => b.UseSqlServer(connectionString); o.ModelConfigurator = mb => mb.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); }); services.AddEfCoreRepository(); // Optional: DDD aggregate repository (Savvyio.Extensions.DependencyInjection.EFCore.Domain) services.AddEfCoreAggregateDataSource(o => { o.ContextConfigurator = b => b.UseSqlServer(connectionString); }); services.AddEfCoreAggregateRepository(); // 3. Use in a command handler public class AccountCommandHandler : CommandHandler { private readonly IWritableRepository _repo; private readonly IUnitOfWork _uow; public AccountCommandHandler( IWritableRepository repo, IUnitOfWork uow) { _repo = repo; _uow = uow; } protected override void RegisterDelegates(IFireForgetRegistry handlers) { handlers.RegisterAsync(async cmd => { await _repo.CreateAsync(new AccountEntity { Id = Guid.NewGuid(), Email = cmd.Email }); await _uow.SaveChangesAsync(); }); } } ``` -------------------------------- ### Bootstrap Savvy.IO with AddSavvyIO Source: https://context7.com/codebeltnet/savvyio/llms.txt Configure Savvy.IO services using AddSavvyIO on IServiceCollection. Supports manual registration or automatic assembly scanning for handlers and dispatchers. ```csharp // Program.cs / Startup.cs services.AddSavvyIO(o => o .AddCommandHandler() .AddCommandDispatcher() .AddQueryHandler() .AddQueryDispatcher() .AddDomainEventHandler() .AddDomainEventDispatcher() .AddIntegrationEventHandler() .AddIntegrationEventDispatcher() .AddMediator() .EnableHandlerServicesDescriptor() // optional diagnostics ); // Automatic discovery alternative (scans calling assembly): services.AddSavvyIO(o => o .UseAutomaticHandlerDiscovery() .UseAutomaticDispatcherDiscovery() ); // Log all registered handlers at startup (requires EnableHandlerServicesDescriptor) var app = builder.Build(); app.Services.WriteHandlerDiscoveriesToLog(); ``` -------------------------------- ### Manual Handler and Dispatcher Registration with SavvyioOptions Source: https://context7.com/codebeltnet/savvyio/llms.txt Configures handlers and dispatchers explicitly using SavvyioOptions, suitable for standalone applications without a DI container. Supports both direct registration and assembly scanning for discovery. ```csharp // Standalone usage (without Microsoft DI) var options = new SavvyioOptions() .AddHandler() .AddDispatcher() .AddHandler() .AddDispatcher() .EnableHandlerServicesDescriptor(); // Validate a type pair manually bool valid = SavvyioOptions.IsValid( typeof(AccountCommandHandler), typeof(ICommandHandler)); Console.WriteLine(valid); // true // Assembly-scan based auto-discovery var autoOptions = new SavvyioOptions() .EnableHandlerDiscovery(true) .EnableDispatcherDiscovery(true); ``` -------------------------------- ### Implement DDD Entity and AggregateRoot Source: https://context7.com/codebeltnet/savvyio/llms.txt Create domain entities with identity-based equality and aggregate roots that manage domain events. Ensure the AggregateRoot base class is used for entities that emit domain events. ```csharp // Entity example public class OrderItem : Entity { public string ProductName { get; private set; } public int Quantity { get; private set; } public OrderItem(Guid id, string productName, int quantity) : base(id) { ProductName = productName; Quantity = quantity; } } ``` ```csharp // Aggregate Root example public class Order : AggregateRoot { private readonly List _items = new(); public Order(Guid id) : base(id) { } public IReadOnlyList Items => _items; public void AddItem(Guid itemId, string product, int qty) { var item = new OrderItem(itemId, product, qty); _items.Add(item); AddEvent(new OrderItemAddedEvent(Id, itemId, product, qty)); } } ``` ```csharp var order = new Order(Guid.NewGuid()); order.AddItem(Guid.NewGuid(), "Widget", 3); Console.WriteLine(order.Events.Count); // 1 Console.WriteLine(order.IsTransient); // false order.RemoveAllEvents(); // clear after dispatch ``` -------------------------------- ### Define and Dispatch CQRS Query Source: https://context7.com/codebeltnet/savvyio/llms.txt Define a query record and its result DTO, implement the handler, and dispatch the query using the IQueryDispatcher. Ensure IAccountReadModel is registered in the service provider. ```csharp // 1. Define the query and its DTO public record GetAccountByIdQuery(Guid AccountId) : Query; public record AccountDto(Guid Id, string Email, string Name); ``` ```csharp // 2. Define the handler public class AccountQueryHandler : QueryHandler { private readonly IAccountReadModel _readModel; public AccountQueryHandler(IAccountReadModel readModel) { _readModel = readModel; } protected override void RegisterDelegates(IRequestReplyRegistry handlers) { handlers.RegisterAsync(HandleGetAccountByIdAsync); } private async Task HandleGetAccountByIdAsync(GetAccountByIdQuery query) { return await _readModel.FindByIdAsync(query.AccountId); } } ``` ```csharp // 3. Dispatch the query and get a result IQueryDispatcher dispatcher = serviceProvider.GetRequiredService(); AccountDto dto = await dispatcher.QueryAsync(new GetAccountByIdQuery(accountId)); Console.WriteLine(dto.Email); // "user@example.com" ``` -------------------------------- ### Implement Command Handler for CQRS Write Operations Source: https://context7.com/codebeltnet/savvyio/llms.txt Define a `Command` for write intentions and a `CommandHandler` to process it. Handlers register typed delegate methods for specific commands. Dispatch commands using `ICommandDispatcher`. ```csharp // 1. Define the command public record CreateAccountCommand(string Email, string Name) : Command; // 2. Define the handler public class AccountCommandHandler : CommandHandler { private readonly IAccountRepository _repo; public AccountCommandHandler(IAccountRepository repo) { _repo = repo; } protected override void RegisterDelegates(IFireForgetRegistry handlers) { handlers.RegisterAsync(HandleCreateAccountAsync); } private async Task HandleCreateAccountAsync(CreateAccountCommand cmd) { var account = new Account(Guid.NewGuid(), cmd.Email, cmd.Name); await _repo.AddAsync(account); } } // 3. Dispatch the command ICommandDispatcher dispatcher = serviceProvider.GetRequiredService(); await dispatcher.CommitAsync(new CreateAccountCommand("user@example.com", "Alice")); ``` -------------------------------- ### Implement ValueObject with Structural Equality Source: https://context7.com/codebeltnet/savvyio/llms.txt Create ValueObjects for DDD by implementing structural equality based on public properties. Use SingleValueObject for convenience with single-property types. ```csharp public record Money : ValueObject { public decimal Amount { get; } public string Currency { get; } public Money(decimal amount, string currency) { Amount = amount; Currency = currency; } } var price1 = new Money(9.99m, "USD"); var price2 = new Money(9.99m, "USD"); var price3 = new Money(14.99m, "EUR"); Console.WriteLine(price1 == price2); // True (value equality) Console.WriteLine(price1 == price3); // False Console.WriteLine(price1.ToString()); // "9.99, USD" public record Email : SingleValueObject { public Email(string value) : base(value) { if (!value.Contains('@')) throw new ArgumentException("Invalid email."); } } var e1 = new Email("user@example.com"); var e2 = new Email("user@example.com"); Console.WriteLine(e1 == e2); // True ``` -------------------------------- ### Unified Dispatching with Mediator Source: https://context7.com/codebeltnet/savvyio/llms.txt Utilize the Mediator facade for single-point dispatching of commands, domain events, integration events, and queries. Supports Fire-and-Forget and Request-Reply patterns. ```csharp IMediator mediator = serviceProvider.GetRequiredService(); // Command (Fire-and-Forget) await mediator.CommitAsync(new CreateAccountCommand("user@example.com", "Alice")); // Domain event (Fire-and-Forget) await mediator.RaiseAsync(new OrderItemAddedEvent(orderId, itemId, "Widget", 3)); // Integration event (Fire-and-Forget) await mediator.PublishAsync(new OrderShippedIntegrationEvent(orderId, "TRACK123")); // Query (Request-Reply) AccountDto account = await mediator.QueryAsync(new GetAccountByIdQuery(accountId)); Console.WriteLine(account.Email); ``` -------------------------------- ### Define Domain Models with Request Base Class Source: https://context7.com/codebeltnet/savvyio/llms.txt All commands, queries, domain events, and integration events should derive from the abstract `Request` record. This base class automatically captures member type and correlation ID in its metadata. ```csharp // Every concrete request automatically sets its MemberType metadata. // Commands, queries, domain events, and integration events all derive from Request. // Custom command public record CreateAccountCommand(string Email, string Name) : Command; // Custom query public record GetAccountByIdQuery(Guid AccountId) : Query; // Custom domain event public record AccountCreatedEvent(Guid AccountId) : DomainEvent; // Custom integration event public record AccountCreatedIntegrationEvent(Guid AccountId, string Email) : IntegrationEvent; // Reading built-in metadata from any IMetadata implementation var cmd = new CreateAccountCommand("user@example.com", "Alice"); string memberType = cmd.GetMemberType(); // "MyApp.CreateAccountCommand, MyApp" string correlationId = cmd.GetCorrelationId(); // auto-generated UUID (N format) ``` -------------------------------- ### Define and Handle Domain Events Source: https://context7.com/codebeltnet/savvyio/llms.txt Define domain events that auto-generate IDs and timestamps. Implement handlers that register delegates for specific event types and are dispatched by DomainEventDispatcher. Ensure IInventoryService is registered. ```csharp // 1. Define domain event public record OrderItemAddedEvent(Guid OrderId, Guid ItemId, string Product, int Qty) : DomainEvent; ``` ```csharp // 2. Define handler public class OrderDomainEventHandler : DomainEventHandler { private readonly IInventoryService _inventory; public OrderDomainEventHandler(IInventoryService inventory) { _inventory = inventory; } protected override void RegisterDelegates(IFireForgetRegistry handlers) { handlers.RegisterAsync(HandleOrderItemAddedAsync); } private async Task HandleOrderItemAddedAsync(OrderItemAddedEvent e) { await _inventory.ReserveAsync(e.ItemId, e.Qty); Console.WriteLine($"Reserved {e.Qty}x {e.Product} (eventId={e.GetEventId()})"); } } ``` ```csharp // 3. Dispatch IDomainEventDispatcher dispatcher = serviceProvider.GetRequiredService(); await dispatcher.RaiseAsync(new OrderItemAddedEvent(orderId, itemId, "Widget", 3)); // Output: Reserved 3x Widget (eventId=) ``` -------------------------------- ### Define and Handle Integration Events Source: https://context7.com/codebeltnet/savvyio/llms.txt Define integration events for cross-service communication and implement handlers using the Fire-and-Forget pattern. Dispatch events using the IntegrationEventDispatcher. ```csharp public record OrderShippedIntegrationEvent(Guid OrderId, string TrackingCode) : IntegrationEvent; public class NotificationIntegrationEventHandler : IntegrationEventHandler { private readonly IEmailService _email; public NotificationIntegrationEventHandler(IEmailService email) { _email = email; } protected override void RegisterDelegates(IFireForgetRegistry handlers) { handlers.RegisterAsync(HandleOrderShippedAsync); } private async Task HandleOrderShippedAsync(OrderShippedIntegrationEvent e) { await _email.SendShippingNotificationAsync(e.OrderId, e.TrackingCode); } } IIntegrationEventDispatcher dispatcher = serviceProvider.GetRequiredService(); await dispatcher.PublishAsync(new OrderShippedIntegrationEvent(orderId, "1Z999AA10123456784")); ``` -------------------------------- ### Fluent Metadata Manipulation with Extension Methods Source: https://context7.com/codebeltnet/savvyio/llms.txt Utilizes extension methods on IMetadata for fluent manipulation of standard and custom metadata. This is useful for tracking request context like correlation and causation IDs. ```csharp var cmd = new CreateAccountCommand("user@example.com", "Alice"); // Set standard metadata cmd.SetCorrelationId("corr-001") .SetCausationId("cause-001") .SetEventId("evt-001") .SetTimestamp(DateTime.UtcNow); // Read standard metadata Console.WriteLine(cmd.GetCorrelationId()); // "corr-001" Console.WriteLine(cmd.GetCausationId()); // "cause-001" Console.WriteLine(cmd.GetEventId()); // "evt-001" Console.WriteLine(cmd.GetMemberType()); // "MyApp.CreateAccountCommand, MyApp" // Custom metadata cmd.SaveMetadata("tenant-id", "tenant-42"); object tenantId = cmd.Metadata["tenant-id"]; // "tenant-42" // Propagate metadata from one object to another (non-destructive copy) var domainEvent = new AccountCreatedEvent(Guid.NewGuid()); domainEvent.MergeMetadata(cmd); // copies correlation/causation IDs into the event ``` -------------------------------- ### Wrap Command in Message for Transport Source: https://context7.com/codebeltnet/savvyio/llms.txt Wraps a command object into a transport-neutral message envelope. Ensure the Savvyio.Commands.Messaging namespace is included. ```csharp using Savvyio.Commands.Messaging; var command = new CreateAccountCommand("user@example.com", "Alice"); // Wrap in a message for out-of-process transport IMessage message = command.ToMessage( source: new Uri("https://myservice.example.com/accounts"), type: "application/json" ); Console.WriteLine(message.Id); // auto-generated UUID Console.WriteLine(message.Source); // "https://myservice.example.com/accounts" Console.WriteLine(message.Type); // "application/json" Console.WriteLine(message.Time); // UTC timestamp Console.WriteLine(message.Data.Email); // "user@example.com" ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.