ModelingEvolution.Booking
1.0.0-preview.2
See the version list below for details.
dotnet add package ModelingEvolution.Booking --version 1.0.0-preview.2
NuGet\Install-Package ModelingEvolution.Booking -Version 1.0.0-preview.2
<PackageReference Include="ModelingEvolution.Booking" Version="1.0.0-preview.2" />
<PackageVersion Include="ModelingEvolution.Booking" Version="1.0.0-preview.2" />
<PackageReference Include="ModelingEvolution.Booking" />
paket add ModelingEvolution.Booking --version 1.0.0-preview.2
#r "nuget: ModelingEvolution.Booking, 1.0.0-preview.2"
#:package ModelingEvolution.Booking@1.0.0-preview.2
#addin nuget:?package=ModelingEvolution.Booking&version=1.0.0-preview.2&prerelease
#tool nuget:?package=ModelingEvolution.Booking&version=1.0.0-preview.2&prerelease
ModelingEvolution.Booking
Meeting booking as an event-sourced module on MicroPlumberd 1.2.x / KurrentDB: one BookingCalendar-{mailbox}
stream arbitrates non-overlap of meetings on a mailbox (optimistic concurrency — a lost version race retries once,
then refuses as slot_taken); each booking lives on its own Booking-{BookingId} stream (MeetingBooked,
MeetingCancelled); availability is synced from O365 and offered per meeting type; bookings carry an
ExternalReference (kind:value) a consumer uses to find them again.
builder.Services.AddPlumberd(KurrentDBClientSettings.Create(connectionString));
builder.Services.AddBooking(builder.Configuration); // command handlers, read models, calendar integration (Booking:* options)
The pipeline as two public primitives (1.0.0-preview.2, ADR-3 split)
A host that only TAKES intents (no calendar credential) and a worker that MATERIALISES them can each compose the
half they need — the website's own BookingCommandHandler runs the very same two objects:
// intent-taking unit: read models + step 1, no ICalendarService
builder.Services.AddSingletonEventHandler<BookingConfigModel>(FromRelativeStreamPosition.End - 1);
builder.Services.AddSingletonEventHandler<AvailableSlotsModel>(FromRelativeStreamPosition.End - 1);
builder.Services.Configure<BookingOptions>(cfg.GetSection("Booking")); // Mailbox ⇒ calendar stream; ReservationLease
builder.Services.AddBookingSlotArbiter();
var hold = await arbiter.ReserveAsync(bookingId, date, time, MeetingTypeKey.Standard); // idempotent for the same id + window; faults: slot_taken | invalid_* | too_close
await arbiter.TryReleaseAsync(bookingId, "reason");
// materialising worker: + ICalendarService (yours) + steps 2–4
builder.Services.AddSingleton<ICalendarService, YourCalendar>(); // or the sink
builder.Services.AddBookingMaterialiser();
var result = await materialiser.MaterialiseAsync(new MeetingIntent(bookingId, date, time, type, name, email, phone, comments, reference, tags, "portal"));
// result.WasAlreadyBooked: a re-delivered intent — nothing created twice; result.Booked is the stored event
MaterialiseAsync checks Booking-{id} for MeetingBooked FIRST, calls CreateEventAsync(…, transactionId: id)
(the calendar's idempotency key — a crash between the calendar and the append cannot orphan a second event on retry),
appends MeetingBooked + BookingTagAttached per key in ONE append, and confirms the hold.
Read models: BookingLookupModel (by id / by tag), AvailableSlotsModel, BookingConfigModel.
The concurrency invariant is tested against a real KurrentDB in the source repository
(RealStoreSlotArbitrationTests), on both the pre- and post-1.2 client libraries.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- MicroPlumberd (>= 1.2.2)
- MicroPlumberd.Services (>= 1.2.2)
- MicroPlumberd.SourceGenerators (>= 1.2.2)
- ModelingEvolution.JsonParsableConverter (>= 1.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-preview.11 | 25 | 8/19/2026 |
| 1.0.0-preview.10 | 28 | 8/19/2026 |
| 1.0.0-preview.9 | 34 | 8/19/2026 |
| 1.0.0-preview.8 | 55 | 8/17/2026 |
| 1.0.0-preview.7 | 51 | 8/17/2026 |
| 1.0.0-preview.6 | 45 | 8/17/2026 |
| 1.0.0-preview.5 | 48 | 8/17/2026 |
| 1.0.0-preview.4 | 53 | 8/17/2026 |
| 1.0.0-preview.3 | 51 | 8/17/2026 |
| 1.0.0-preview.2 | 56 | 8/17/2026 |
| 1.0.0-preview.1 | 52 | 8/17/2026 |