Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

stevsharp/OutboxPattern

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

8 Commits

Repository files navigation

🧩 Domain Events & Outbox Pattern (.NET 9)

This challenge demonstrates how to implement Domain Events and the Outbox Pattern using Entity Framework Core with SQLite and Background Services in .NET 9.

It ensures domain events are saved atomically with aggregate changes and then published reliably from an Outbox table by a background dispatcher.


πŸš€ Tech Stack

Component Purpose
.NET 9 Framework
EF Core + SQLite Persistence layer
SaveChangesInterceptor Captures domain events before save
BackgroundService Dispatches outbox messages
System.Text.Json Serializes domain events
Swashbuckle.AspNetCore Swagger UI for testing endpoints

πŸ—οΈ Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ API + EF Core β”‚
β”‚ ───────────────────────────────────── β”‚
β”‚ Order Aggregate raises Domain Event β”‚
β”‚ β†’ EF Interceptor saves Outbox row β”‚
β”‚ β†’ Transaction commits atomically β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β”‚
 β–Ό
 πŸ“¨ Outbox Dispatcher (Hosted Service)
 β”œβ”€ Reads unprocessed messages
 β”œβ”€ Publishes events (console bus)
 └─ Marks as processed

πŸ“‚ Project Structure

Challenge05_Outbox/
β”‚
β”œβ”€β”€ Program.cs
β”‚
β”œβ”€β”€ Domain/
β”‚ β”œβ”€β”€ DomainEvent.cs
β”‚ β”œβ”€β”€ IHasDomainEvents.cs
β”‚ β”œβ”€β”€ Order.cs
β”‚ └── OrderPlacedDomainEvent.cs
β”‚
└── Infrastructure/
 β”œβ”€β”€ AppDbContext.cs
 β”œβ”€β”€ OutboxMessage.cs
 β”œβ”€β”€ OutboxDispatcher.cs
 β”œβ”€β”€ OutboxSaveChangesInterceptor.cs
 β”œβ”€β”€ IEventSerializer.cs
 └── SystemTextJsonEventSerializer.cs

βš™οΈ Setup Instructions

1️⃣ Clone the Repository

git clone https://github.com/YOUR_USERNAME/Challenge05_Outbox.git
cd Challenge05_Outbox

2️⃣ Install Dependencies

dotnet restore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Swashbuckle.AspNetCore

3️⃣ Create and Apply Migration

dotnet tool install --global dotnet-ef
dotnet ef migrations add Init
dotnet ef database update

This will create a local SQLite file: challenge05.db


🧩 Run the Project

dotnet run

Open:


πŸ§ͺ Try It Out

βž• Create an Order

curl -X POST "http://localhost:5000/orders?total=49.99"

πŸ“¬ Check Outbox Messages

curl "http://localhost:5000/outbox"

Console output example:

[BUS] βœ… OrderPlaced published => OrderId=6fdc..., Total=49.99

πŸ’Ύ Database Schema (Simplified)

Table Purpose
Orders Domain aggregate
OutboxMessages Stores serialized domain events

SQLite DDL:

CREATE TABLE "OutboxMessages" (
 "Id" INTEGER PRIMARY KEY AUTOINCREMENT,
 "OccurredOnUtc" TEXT NOT NULL,
 "Type" TEXT NOT NULL,
 "Payload" TEXT NOT NULL,
 "ProcessedOnUtc" TEXT NULL,
 "Error" TEXT NULL
);
CREATE INDEX "IX_OutboxMessages_ProcessedOnUtc"
ON "OutboxMessages" ("ProcessedOnUtc");

πŸ” Event Flow

  1. Aggregate raises event

    AddEvent(new OrderPlacedDomainEvent(Id, Total));
  2. Interceptor captures it during SaveChangesAsync and writes an OutboxMessage in the same transaction.

  3. Background service polls for unprocessed messages and "publishes" them (here, simulated with console output).

  4. Marks processed β†’ ProcessedOnUtc is updated.


🧠 Key Files

  • OutboxSaveChangesInterceptor.cs β†’ Captures domain events pre-save
  • OutboxDispatcher.cs β†’ Processes messages asynchronously
  • SystemTextJsonEventSerializer.cs β†’ Serializes/deserializes events
  • AppDbContext.cs β†’ Entity mappings + EF setup

πŸ“¦ NuGet Packages

Package Purpose
Microsoft.EntityFrameworkCore.Sqlite SQLite provider
Microsoft.EntityFrameworkCore.Design CLI migrations
Swashbuckle.AspNetCore Swagger documentation
Microsoft.Extensions.Hosting Background services
Microsoft.Extensions.Logging.Console Logging

🧱 Future Improvements

  • Add retry & backoff policy in dispatcher
  • Use a real message bus (e.g., RabbitMQ or Kafka)
  • Introduce event versioning and handlers registry
  • Add unit tests with InMemory EF provider

πŸ“œ License

This project is licensed under the MIT License. Feel free to use / modify / share.


πŸ‘€ Author

Spyros Ponaris πŸ’Ό LinkedIn


About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /