public CustomerCommands(EventStreamCollection <Customer> eventStreamManagersCollection, CustomerQueries customerQueries) { managers = eventStreamManagersCollection; queries = customerQueries; }
static async Task Main(string[] args) { Console.WriteLine("\nEventStreamDotNet demo\n"); // The library can optionally be configured with any implementation of the // standard Microsoft ILoggerFactory to enable debug-level log output from // the library. Here we're using Serilog but Microsoft's own basic logger // or other libraries like NLog could be used. Debug logging can generate // significant amounts of log output, only enable it in the library if you // absolutely need it. var loggerFactory = new LoggerFactory() .AddSerilog(new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.Console() .CreateLogger()); try { // Populate the EventStreamDotNet configuration classes from appsettings.json. This could // also populate any application-specific configuration systems using the same process. AppConfig.LoadConfiguration(); Console.WriteLine($"Database: {AppConfig.Get.EventStreamDotNet.Database.ConnectionString}"); Console.WriteLine($"Log Table: {AppConfig.Get.EventStreamDotNet.Database.EventTableName}"); Console.WriteLine($"Snapshot Table: {AppConfig.Get.EventStreamDotNet.Database.SnapshotTableName}"); // Optionally reset the database Console.Write("\nDelete all records from the demo database (Y/N)? "); var key = Console.ReadKey(true); if (key.Key.Equals(ConsoleKey.Y)) { Console.WriteLine("YES\n"); await ResetDatabase(); } else { Console.WriteLine("NO\n"); } // The demo will use the library's collection class to interact with the event stream. Declare // it now because we'll obtain it differently depending on whether we use dependency injection. EventStreamCollection <Customer> customerManagers; // Optionally use dependency injection. Console.Write("Use dependency injection (Y/N)? "); key = Console.ReadKey(true); // Using dependency injection if (key.Key.Equals(ConsoleKey.Y)) { Console.WriteLine("YES\n"); // Configure and register the library services var services = new ServiceCollection(); services.AddEventStreamDotNet( loggerFactory: loggerFactory, domainModelConfigs: cfg => { cfg.AddConfiguration <Customer>(AppConfig.Get.EventStreamDotNet); }, domainEventHandlers: cfg => { cfg.RegisterDomainEventHandler <Customer, CustomerEventHandler>(); }, projectionHandlers: cfg => { cfg.RegisterProjectionHandler <Customer, CustomerProjectionHandler>(); }); // Register the domain model's event stream collection for DI services.AddSingleton <EventStreamCollection <Customer> >(); // Create the DI service provider. var serviceProvider = services.BuildServiceProvider(); // Because we're doing a simple console demo, only the library is using DI to // resolve references; we'll go ahead and do it the "anti-pattern" way for brevity. // However, this does actually use DI, the EventStreamCollection constructor has a // dependency on the three library services registered above, and of course, the // collection itself was just registered above as a singleton service. customerManagers = serviceProvider.GetService <EventStreamCollection <Customer> >(); } // Not using dependency injection else { Console.WriteLine("NO\n"); // Configure the library services var eventServices = new DirectDependencyServiceHost( loggerFactory: loggerFactory, domainModelConfigs: cfg => { cfg.AddConfiguration <Customer>(AppConfig.Get.EventStreamDotNet); }, domainEventHandlers: cfg => { cfg.RegisterDomainEventHandler <Customer, CustomerEventHandler>(); }, projectionHandlers: cfg => { cfg.RegisterProjectionHandler <Customer, CustomerProjectionHandler>(); }); // Create the event stream collection. customerManagers = new EventStreamCollection <Customer>(eventServices); } // The rest of the demo works the same way with or without the use of dependency injection. // This is just one possible usage pattern. The thinking here is that a CQRS service will // execute commands against a collection of event streams tied to the same domain model, and // will execute queries against snapshots and projections for that domain model. Probably a // production app would not start a CQRS query service with the EventStreamDotNet configuration, // however (it does not, for example, specify projection table names, which might even be stored // into a completely separate database for performance reasons). var customerQueries = new CustomerQueries(customerManagers); var customerCommands = new CustomerCommands(customerManagers, customerQueries); var customerId = "12345678"; // This is a simple select against the event stream to check whether the ID has ever been used. var customerExists = await customerQueries.CustomerExists(customerId); Console.WriteLine($"Customer id {customerId} exists? {customerExists.Output}"); // When queries and commands succeed, the response is usually an updated snapshot of the dmain data. APIResult <Customer> result; // Create or read the customer record if (!customerExists.Output) { var residence = new Address { Street = "10 Main St.", City = "Anytown", StateOrProvince = "TX", PostalCode = "90210", Country = "USA" }; var person = new Person { FullName = "John Doe", FirstName = "John", LastName = "Doe", Residence = residence, TaxId = "555-55-1234", DateOfBirth = DateTimeOffset.Parse("05/01/1960") }; Console.WriteLine("Creating new customer."); result = await customerCommands.NewCustomer(customerId, person, residence); if (!result.Success) { throw new Exception($"Failed to create new customer: {result.Message}"); } } else { Console.WriteLine("Retrieving customer."); result = await customerQueries.FindCustomer(customerId); if (!result.Success) { throw new Exception($"Failed to retrieve customer snapshot: {result.Message}"); } } // Add or remove a spouse var customer = result.Output; if (customer.Spouse == null) { var spouse = new Person { FullName = "Jane Doe", FirstName = "Jane", LastName = "Doe", Residence = customer.PrimaryAccountHolder.Residence, TaxId = "333-99-4321", DateOfBirth = DateTimeOffset.Parse("11/01/1960") }; Console.WriteLine("Adding spouse (yay)."); result = await customerCommands.UpdateSpouse(customerId, spouse); if (!result.Success) { throw new Exception($"Failed to add spouse: {result.Message}"); } } else { Console.WriteLine("Removing spouse (boo)."); result = await customerCommands.UpdateSpouse(customerId, null); if (!result.Success) { throw new Exception($"Failed to remove spouse: {result.Message}"); } } } catch (Exception ex) { Console.WriteLine($"\nException caught in Program.Main:\n{ex.Message}"); if (ex.InnerException != null) { Console.WriteLine(ex.InnerException.Message); } } finally { // Purge any pending log entries and dispose. Log.CloseAndFlush(); } if (!Debugger.IsAttached) { Console.WriteLine("\n\nPress any key to exit..."); Console.ReadKey(true); } }