-
-
Notifications
You must be signed in to change notification settings - Fork 99
Host the KurrentDB Bookings sample in Aspire with blob storage projections #574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| using System.Runtime.InteropServices; | ||
| using Scalar.Aspire; | ||
|
|
||
| var builder = DistributedApplication.CreateBuilder(args); | ||
|
|
||
| // Same image as the repository's KurrentDB test fixtures | ||
| var kurrentImage = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 | ||
| ? "kurrentplatform/kurrentdb:26.1.1-experimental-arm64-10.0-noble" | ||
| : "kurrentplatform/kurrentdb:26.1.1"; | ||
| var imageParts = kurrentImage.Split(':'); | ||
|
|
||
| var kurrentdb = builder.AddContainer("kurrentdb", imageParts[0], imageParts[1]) | ||
| .WithArgs("--insecure", "--run-projections=All", "--enable-atom-pub-over-http") | ||
| .WithHttpEndpoint(port: 2113, targetPort: 2113, name: "http"); | ||
|
|
||
| var kurrentdbEndpoint = kurrentdb.GetEndpoint("http"); | ||
|
|
||
| var mongoUser = builder.AddParameter("mongo-user", "mongoadmin"); | ||
| var mongoPassword = builder.AddParameter("mongo-password", "secret", secret: true); | ||
|
|
||
| var mongo = builder.AddMongoDB("mongo", userName: mongoUser, password: mongoPassword) | ||
| // MongoDB 8.3 refuses to start on Linux kernel 6.19+ (SERVER-121912) | ||
| .WithImageTag("7.0"); | ||
|
|
||
| var storage = builder.AddAzureStorage("storage").RunAsEmulator(); | ||
| var blobs = storage.AddBlobs("blobs"); | ||
| storage.AddBlobContainer("bookings-container", blobContainerName: "bookings"); | ||
|
|
||
| var bookings = builder.AddProject<Projects.Bookings>("bookings") | ||
| .WithHttpEndpoint() | ||
| .WithHttpHealthCheck("/health") | ||
| .WithReference(blobs) | ||
| .WithEnvironment(ctx => { | ||
| ctx.EnvironmentVariables["KurrentDB__ConnectionString"] = ReferenceExpression.Create( | ||
| $"kurrentdb://{kurrentdbEndpoint.Property(EndpointProperty.Host)}:{kurrentdbEndpoint.Property(EndpointProperty.Port)}?tls=false" | ||
| ); | ||
| ctx.EnvironmentVariables["Mongo__ConnectionString"] = mongo.Resource.ConnectionStringExpression; | ||
| } | ||
| ) | ||
| .WaitFor(kurrentdb) | ||
| .WaitFor(mongo) | ||
| .WaitFor(blobs); | ||
|
|
||
| var payments = builder.AddProject<Projects.Bookings_Payments>("payments") | ||
| .WithHttpEndpoint() | ||
| .WithHttpHealthCheck("/health") | ||
| .WithEnvironment(ctx => { | ||
| ctx.EnvironmentVariables["KurrentDB__ConnectionString"] = ReferenceExpression.Create( | ||
| $"kurrentdb://{kurrentdbEndpoint.Property(EndpointProperty.Host)}:{kurrentdbEndpoint.Property(EndpointProperty.Port)}?tls=false" | ||
| ); | ||
| ctx.EnvironmentVariables["Mongo__ConnectionString"] = mongo.Resource.ConnectionStringExpression; | ||
| } | ||
| ) | ||
| .WaitFor(kurrentdb) | ||
| .WaitFor(mongo); | ||
|
|
||
| builder.AddScalarApiReference() | ||
| .WithApiReference(bookings) | ||
| .WithApiReference(payments) | ||
| .WaitFor(bookings) | ||
| .WaitFor(payments); | ||
|
|
||
| builder.Build().Run(); |
19 changes: 19 additions & 0 deletions
19
samples/kurrentdb/Bookings.AppHost/Bookings.AppHost.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <Project Sdk="Aspire.AppHost.Sdk/13.4.6"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <UserSecretsId>830ae8e6-48ba-4d84-b460-a2922dc3ec63</UserSecretsId> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\Bookings\Bookings.csproj"/> | ||
| <ProjectReference Include="..\Bookings.Payments\Bookings.Payments.csproj"/> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Aspire.Hosting.Azure.Storage"/> | ||
| <PackageReference Include="Aspire.Hosting.MongoDB"/> | ||
| <PackageReference Include="Scalar.Aspire"/> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 22 additions & 1 deletion
23
samples/kurrentdb/Bookings/Application/BookingsQueryService.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,30 @@ | ||
| using Azure; | ||
| using Azure.Storage.Blobs; | ||
| using Bookings.Application.Queries; | ||
| using Eventuous.Azure.Storage.Blobs; | ||
| using Eventuous.Projections.MongoDB.Tools; | ||
| using MongoDB.Driver; | ||
|
|
||
| namespace Bookings.Application; | ||
|
|
||
| public class BookingsQueryService(IMongoDatabase database) { | ||
| public class BookingsQueryService(IMongoDatabase database, BlobServiceClient blobClient, BlobStorageProjectorOptions blobOptions) { | ||
| public async Task<MyBookings?> GetUserBookings(string userId) => await database.LoadDocument<MyBookings>(userId); | ||
|
|
||
| /// <summary> | ||
| /// Reads the booking state projected to Azure Blob Storage. The blob name follows the | ||
| /// projector's default naming convention: {id}/{state type name}.json. | ||
| /// </summary> | ||
| public async Task<BookingView?> GetBooking(string bookingId, CancellationToken cancellationToken) { | ||
| var blob = blobClient | ||
| .GetBlobContainerClient(BookingStateBlobProjection.ContainerName) | ||
| .GetBlobClient($"{bookingId}/{nameof(BookingView)}.json"); | ||
|
|
||
| try { | ||
| var content = await blob.DownloadContentAsync(cancellationToken); | ||
|
|
||
| return content.Value.Content.ToObjectFromJson<BookingView>(blobOptions.JsonOptions); | ||
| } catch (RequestFailedException e) when (e.Status == 404) { | ||
| return null; | ||
| } | ||
| } | ||
| } |
34 changes: 34 additions & 0 deletions
34
samples/kurrentdb/Bookings/Application/Queries/BookingStateBlobProjection.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| using Azure.Storage.Blobs; | ||
| using Eventuous.Azure.Storage.Blobs; | ||
| using static Bookings.Domain.Bookings.BookingEvents; | ||
|
|
||
| namespace Bookings.Application.Queries; | ||
|
|
||
| /// <summary> | ||
| /// Projects the booking state to Azure Blob Storage, in parallel with the MongoDB projections. | ||
| /// Each booking stream becomes one JSON blob. It runs on its own all-stream subscription with | ||
| /// its own checkpoint, so it can replay from the beginning of the stream and backfill the blobs | ||
| /// when added to an existing system. The all-stream subscription provides real global positions, | ||
| /// so the projector can use ByGlobalPosition idempotency to skip replayed events. | ||
| /// </summary> | ||
| public class BookingStateBlobProjection : BlobStorageProjector<BookingView> { | ||
| public const string ContainerName = "bookings"; | ||
|
|
||
| public BookingStateBlobProjection(BlobServiceClient client, BlobStorageProjectorOptions options) | ||
| : base(client, ContainerName, options) { | ||
| On<V1.RoomBooked>((ctx, view) => view with { | ||
| Id = ctx.Stream.GetId(), | ||
| GuestId = ctx.Message.GuestId, | ||
| RoomId = ctx.Message.RoomId, | ||
| CheckInDate = ctx.Message.CheckInDate, | ||
| CheckOutDate = ctx.Message.CheckOutDate, | ||
| BookingPrice = ctx.Message.BookingPrice, | ||
| Outstanding = ctx.Message.OutstandingAmount | ||
| } | ||
| ); | ||
|
|
||
| On<V1.PaymentRecorded>((view, evt) => view with { Outstanding = evt.Outstanding }); | ||
|
|
||
| On<V1.BookingFullyPaid>((view, _) => view with { Paid = true }); | ||
| } | ||
| } |
19 changes: 19 additions & 0 deletions
19
samples/kurrentdb/Bookings/Application/Queries/BookingView.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| using NodaTime; | ||
|
|
||
| // ReSharper disable UnusedAutoPropertyAccessor.Global | ||
| namespace Bookings.Application.Queries; | ||
|
|
||
| /// <summary> | ||
| /// Booking state projected to Azure Blob Storage, one blob per booking stream. | ||
| /// Requires a parameterless constructor, as the blob projector creates a new instance for new blobs. | ||
| /// </summary> | ||
| public record BookingView { | ||
| public string Id { get; init; } = ""; | ||
| public string GuestId { get; init; } = ""; | ||
| public string RoomId { get; init; } = ""; | ||
| public LocalDate CheckInDate { get; init; } | ||
| public LocalDate CheckOutDate { get; init; } | ||
| public float BookingPrice { get; init; } | ||
| public float Outstanding { get; init; } | ||
| public bool Paid { get; init; } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Known and deferred: the Zipkin exporter is the sample's pre-existing standalone tracing fallback (the Bookings service has the identical call, and the compose stack ships Zipkin). Under Aspire both services already export via OTLP. Replacing the standalone observability stack is out of scope for this PR.