From 337cf615cdf1b771a1fe73dd80021d68f73c3876 Mon Sep 17 00:00:00 2001 From: MilePrivate Date: Thu, 13 Aug 2026 11:00:58 +0200 Subject: [PATCH] starting mongoDb read model --- .../Configuration/MongoDbOptions.cs | 12 +++++ .../OrderProcessing.ReadModelWorker.csproj | 18 +++++++ .../Persistence/MongoDbInitializer.cs | 48 +++++++++++++++++++ .../Persistence/OrderReadModelStore.cs | 20 ++++++++ OrderProcessing.ReadModelWorker/Program.cs | 31 ++++++++++++ .../Properties/launchSettings.json | 12 +++++ .../ReadModels/OrderItemReadModel.cs | 14 ++++++ .../ReadModels/OrderReadModel.cs | 27 +++++++++++ .../appsettings.Development.json | 8 ++++ .../appsettings.json | 13 +++++ OrderProcessingPlatform.slnx | 1 + 11 files changed, 204 insertions(+) create mode 100644 OrderProcessing.ReadModelWorker/Configuration/MongoDbOptions.cs create mode 100644 OrderProcessing.ReadModelWorker/OrderProcessing.ReadModelWorker.csproj create mode 100644 OrderProcessing.ReadModelWorker/Persistence/MongoDbInitializer.cs create mode 100644 OrderProcessing.ReadModelWorker/Persistence/OrderReadModelStore.cs create mode 100644 OrderProcessing.ReadModelWorker/Program.cs create mode 100644 OrderProcessing.ReadModelWorker/Properties/launchSettings.json create mode 100644 OrderProcessing.ReadModelWorker/ReadModels/OrderItemReadModel.cs create mode 100644 OrderProcessing.ReadModelWorker/ReadModels/OrderReadModel.cs create mode 100644 OrderProcessing.ReadModelWorker/appsettings.Development.json create mode 100644 OrderProcessing.ReadModelWorker/appsettings.json diff --git a/OrderProcessing.ReadModelWorker/Configuration/MongoDbOptions.cs b/OrderProcessing.ReadModelWorker/Configuration/MongoDbOptions.cs new file mode 100644 index 0000000..21e7fdf --- /dev/null +++ b/OrderProcessing.ReadModelWorker/Configuration/MongoDbOptions.cs @@ -0,0 +1,12 @@ +namespace OrderProcessing.ReadModelWorker.Configuration; + +public sealed class MongoDbOptions +{ + public const string SectionName = "MongoDb"; + + public string ConnectionString { get; set; } = "mongodb://localhost:27017"; + + public string DatabaseName { get; set; } = "OrderProcessingReadDb"; + + public string OrdersCollectionName { get; set; } = "orders"; +} \ No newline at end of file diff --git a/OrderProcessing.ReadModelWorker/OrderProcessing.ReadModelWorker.csproj b/OrderProcessing.ReadModelWorker/OrderProcessing.ReadModelWorker.csproj new file mode 100644 index 0000000..57d3099 --- /dev/null +++ b/OrderProcessing.ReadModelWorker/OrderProcessing.ReadModelWorker.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + dotnet-OrderProcessing.ReadModelWorker-aa56661c-af35-4226-adc4-a3e186dd7a2f + + + + + + + + + + + diff --git a/OrderProcessing.ReadModelWorker/Persistence/MongoDbInitializer.cs b/OrderProcessing.ReadModelWorker/Persistence/MongoDbInitializer.cs new file mode 100644 index 0000000..e99b9c5 --- /dev/null +++ b/OrderProcessing.ReadModelWorker/Persistence/MongoDbInitializer.cs @@ -0,0 +1,48 @@ +using MongoDB.Driver; +using OrderProcessing.ReadModelWorker.ReadModels; + +namespace OrderProcessing.ReadModelWorker.Persistence; + +public sealed class MongoDbInitializer : IHostedService +{ + private readonly OrderReadModelStore _store; + private readonly ILogger _logger; + + public MongoDbInitializer(OrderReadModelStore store, ILogger logger) + { + _store = store; + _logger = logger; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + var indexes = new[] + { + new CreateIndexModel( + Builders + .IndexKeys + .Descending(order => order.CreatedAtUtc)), + + new CreateIndexModel( + Builders + .IndexKeys + .Ascending(order => order.CustomerId) + .Descending(order => order.CreatedAtUtc)), + + new CreateIndexModel( + Builders + .IndexKeys + .Ascending(order => order.Status) + .Descending(order => order.CreatedAtUtc)) + }; + + await _store.Orders.Indexes.CreateManyAsync(indexes, cancellationToken); + + _logger.LogInformation("MongoDB order read-model indexes ensured"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/OrderProcessing.ReadModelWorker/Persistence/OrderReadModelStore.cs b/OrderProcessing.ReadModelWorker/Persistence/OrderReadModelStore.cs new file mode 100644 index 0000000..d36ad79 --- /dev/null +++ b/OrderProcessing.ReadModelWorker/Persistence/OrderReadModelStore.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using OrderProcessing.ReadModelWorker.Configuration; +using OrderProcessing.ReadModelWorker.ReadModels; + +namespace OrderProcessing.ReadModelWorker.Persistence; + +public sealed class OrderReadModelStore +{ + public IMongoCollection Orders { get; } + + public OrderReadModelStore(IMongoClient mongoClient, IOptions options) + { + var mongoOptions = options.Value; + + var database = mongoClient.GetDatabase(mongoOptions.DatabaseName); + + Orders = database.GetCollection(mongoOptions.OrdersCollectionName); + } +} \ No newline at end of file diff --git a/OrderProcessing.ReadModelWorker/Program.cs b/OrderProcessing.ReadModelWorker/Program.cs new file mode 100644 index 0000000..cd1e697 --- /dev/null +++ b/OrderProcessing.ReadModelWorker/Program.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using OrderProcessing.ReadModelWorker.Configuration; +using OrderProcessing.ReadModelWorker.Persistence; + +var builder = Host.CreateApplicationBuilder(args); + +builder.Services.AddOptions() + .Bind(builder.Configuration.GetSection(MongoDbOptions.SectionName)) + .Validate(options => !string.IsNullOrWhiteSpace(options.ConnectionString), + "MongoDB connection string is required.") + .Validate(options => !string.IsNullOrWhiteSpace(options.DatabaseName), + "MongoDB database name is required.") + .Validate(options => !string.IsNullOrWhiteSpace(options.OrdersCollectionName), + "MongoDB orders collection name is required.") + .ValidateOnStart(); + +builder.Services.AddSingleton(serviceProvider => + { + var options = serviceProvider.GetRequiredService>().Value; + + return new MongoClient(options.ConnectionString); + }); + +builder.Services.AddHostedService(); + +builder.Services.AddSingleton(); + +var host = builder.Build(); + +await host.RunAsync(); \ No newline at end of file diff --git a/OrderProcessing.ReadModelWorker/Properties/launchSettings.json b/OrderProcessing.ReadModelWorker/Properties/launchSettings.json new file mode 100644 index 0000000..f981c90 --- /dev/null +++ b/OrderProcessing.ReadModelWorker/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "OrderProcessing.ReadModelWorker": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/OrderProcessing.ReadModelWorker/ReadModels/OrderItemReadModel.cs b/OrderProcessing.ReadModelWorker/ReadModels/OrderItemReadModel.cs new file mode 100644 index 0000000..1a38816 --- /dev/null +++ b/OrderProcessing.ReadModelWorker/ReadModels/OrderItemReadModel.cs @@ -0,0 +1,14 @@ +namespace OrderProcessing.ReadModelWorker.ReadModels; + +public sealed class OrderItemReadModel +{ + public int ProductId { get; set; } + + public required string ProductName { get; set; } + + public int Quantity { get; set; } + + public decimal UnitPrice { get; set; } + + public decimal LineTotal { get; set; } +} \ No newline at end of file diff --git a/OrderProcessing.ReadModelWorker/ReadModels/OrderReadModel.cs b/OrderProcessing.ReadModelWorker/ReadModels/OrderReadModel.cs new file mode 100644 index 0000000..7db2ded --- /dev/null +++ b/OrderProcessing.ReadModelWorker/ReadModels/OrderReadModel.cs @@ -0,0 +1,27 @@ +using MongoDB.Bson.Serialization.Attributes; + +namespace OrderProcessing.ReadModelWorker.ReadModels; + +public sealed class OrderReadModel +{ + [BsonId] + public int OrderId { get; set; } + + public int CustomerId { get; set; } + + public required string CustomerName { get; set; } + + public required string Status { get; set; } + + public decimal TotalAmount { get; set; } + + public DateTime CreatedAtUtc { get; set; } + + public DateTime? CompletedAtUtc { get; set; } + + public DateTime? CancelledAtUtc { get; set; } + + public List Items { get; set; } = []; + + public DateTime LastUpdatedAtUtc { get; set; } +} \ No newline at end of file diff --git a/OrderProcessing.ReadModelWorker/appsettings.Development.json b/OrderProcessing.ReadModelWorker/appsettings.Development.json new file mode 100644 index 0000000..b2dcdb6 --- /dev/null +++ b/OrderProcessing.ReadModelWorker/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/OrderProcessing.ReadModelWorker/appsettings.json b/OrderProcessing.ReadModelWorker/appsettings.json new file mode 100644 index 0000000..47c79fc --- /dev/null +++ b/OrderProcessing.ReadModelWorker/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "MongoDb": { + "ConnectionString": "mongodb://localhost:27017", + "DatabaseName": "OrderProcessingReadDb", + "OrdersCollectionName": "orders" + } +} diff --git a/OrderProcessingPlatform.slnx b/OrderProcessingPlatform.slnx index c1d1b00..92c5ba9 100644 --- a/OrderProcessingPlatform.slnx +++ b/OrderProcessingPlatform.slnx @@ -6,4 +6,5 @@ +