Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions OrderProcessing.ReadModelWorker/Configuration/MongoDbOptions.cs
Original file line number Diff line number Diff line change
@@ -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";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-OrderProcessing.ReadModelWorker-aa56661c-af35-4226-adc4-a3e186dd7a2f</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.9" />
<PackageReference Include="MongoDB.Driver" Version="3.11.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\OrderProcessing.Contracts\OrderProcessing.Contracts.csproj" />
</ItemGroup>
</Project>
48 changes: 48 additions & 0 deletions OrderProcessing.ReadModelWorker/Persistence/MongoDbInitializer.cs
Original file line number Diff line number Diff line change
@@ -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<MongoDbInitializer> _logger;

public MongoDbInitializer(OrderReadModelStore store, ILogger<MongoDbInitializer> logger)
{
_store = store;
_logger = logger;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
var indexes = new[]
{
new CreateIndexModel<OrderReadModel>(
Builders<OrderReadModel>
.IndexKeys
.Descending(order => order.CreatedAtUtc)),

new CreateIndexModel<OrderReadModel>(
Builders<OrderReadModel>
.IndexKeys
.Ascending(order => order.CustomerId)
.Descending(order => order.CreatedAtUtc)),

new CreateIndexModel<OrderReadModel>(
Builders<OrderReadModel>
.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;
}
}
20 changes: 20 additions & 0 deletions OrderProcessing.ReadModelWorker/Persistence/OrderReadModelStore.cs
Original file line number Diff line number Diff line change
@@ -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<OrderReadModel> Orders { get; }

public OrderReadModelStore(IMongoClient mongoClient, IOptions<MongoDbOptions> options)
{
var mongoOptions = options.Value;

var database = mongoClient.GetDatabase(mongoOptions.DatabaseName);

Orders = database.GetCollection<OrderReadModel>(mongoOptions.OrdersCollectionName);
}
}
31 changes: 31 additions & 0 deletions OrderProcessing.ReadModelWorker/Program.cs
Original file line number Diff line number Diff line change
@@ -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<MongoDbOptions>()
.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<IMongoClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<MongoDbOptions>>().Value;

return new MongoClient(options.ConnectionString);
});

builder.Services.AddHostedService<MongoDbInitializer>();

builder.Services.AddSingleton<OrderReadModelStore>();

var host = builder.Build();

await host.RunAsync();
12 changes: 12 additions & 0 deletions OrderProcessing.ReadModelWorker/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"OrderProcessing.ReadModelWorker": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}
14 changes: 14 additions & 0 deletions OrderProcessing.ReadModelWorker/ReadModels/OrderItemReadModel.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
27 changes: 27 additions & 0 deletions OrderProcessing.ReadModelWorker/ReadModels/OrderReadModel.cs
Original file line number Diff line number Diff line change
@@ -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<OrderItemReadModel> Items { get; set; } = [];

public DateTime LastUpdatedAtUtc { get; set; }
}
8 changes: 8 additions & 0 deletions OrderProcessing.ReadModelWorker/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
13 changes: 13 additions & 0 deletions OrderProcessing.ReadModelWorker/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"MongoDb": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "OrderProcessingReadDb",
"OrdersCollectionName": "orders"
}
}
1 change: 1 addition & 0 deletions OrderProcessingPlatform.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
<Project Path="OrderProcessing.Api/OrderProcessing.Api.csproj" Id="d38b0644-7c76-43b8-a7ec-51d17124f79b" />
<Project Path="OrderProcessing.Contracts/OrderProcessing.Contracts.csproj" Id="a0233be0-8557-4f97-8542-e65d83a77fd8" />
<Project Path="OrderProcessing.EmailWorker/OrderProcessing.EmailWorker.csproj" Id="0063f556-d348-4cf3-a557-7b2b6a7a8dfa" />
<Project Path="OrderProcessing.ReadModelWorker/OrderProcessing.ReadModelWorker.csproj" Id="fa07bad6-4585-4908-b9ca-c4fa82cec264" />
</Solution>