Notifications and Events
Publish domain events with MediatR.
Notifications and Events is a free C# Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Requests vs Notifications
A request has one handler and returns a value. A notification is a broadcast: it has zero or more handlers and returns nothing. Use notifications to announce that something happened.
// Request: one handler, returns a result
// Notification: many handlers, fire-and-forgetINotification
A domain event is a record implementing INotification. It describes a fact in the past tense.
public record OrderPlacedNotification(int OrderId, int CustomerId)
: INotification;INotificationHandler
Each subscriber implements INotificationHandler<TNotification>. Many handlers can subscribe to the same notification, each handling it independently.
public class SendConfirmationEmailHandler
: INotificationHandler<OrderPlacedNotification>
{
public Task Handle(
OrderPlacedNotification n, CancellationToken ct)
{
// send the email...
return Task.CompletedTask;
}
}Multiple Subscribers
Add as many handlers as you need. None of them know about the others - new reactions can be added without touching the publisher.
public class UpdateInventoryHandler
: INotificationHandler<OrderPlacedNotification>
{
public Task Handle(OrderPlacedNotification n, CancellationToken ct)
{
// decrement stock...
return Task.CompletedTask;
}
}Publishing
To raise a notification, inject IPublisher (or IMediator) and call Publish. Every registered handler is invoked.
public class CreateOrderHandler(AppDbContext db, IPublisher publisher)
: IRequestHandler<CreateOrderCommand, int>
{
public async Task<int> Handle(CreateOrderCommand c, CancellationToken ct)
{
var order = new Order { CustomerId = c.CustomerId };
db.Orders.Add(order);
await db.SaveChangesAsync(ct);
await publisher.Publish(
new OrderPlacedNotification(order.Id, c.CustomerId), ct);
return order.Id;
}
}Decoupling Side Effects
Notifications keep the main use case focused. Placing an order just records the order and publishes an event; emailing and inventory live in their own handlers. This is the open/closed principle in action.
// CreateOrderHandler does NOT know about email or inventoryPublish Strategy
By default MediatR awaits each notification handler sequentially. If one throws, later handlers do not run. You can supply a custom publisher to run them in parallel.
builder.Services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
cfg.NotificationPublisher = new TaskWhenAllPublisher();
});Handling Failures
Because handlers are independent, decide what a failure means. For non-critical side effects, catch and log inside the handler so one failure does not break the others.
public async Task Handle(OrderPlacedNotification n, CancellationToken ct)
{
try { await _email.SendAsync(n.CustomerId, ct); }
catch (Exception ex) { _logger.LogError(ex, "email failed"); }
}Domain Events Pattern
A common pattern: entities collect domain events, and after saving the transaction you publish them all. This guarantees events fire only when the change is committed.
await db.SaveChangesAsync(ct);
foreach (var domainEvent in order.Events)
await publisher.Publish(domainEvent, ct);
order.Events.Clear();Notifications vs Message Queues
MediatR notifications are in-process and synchronous within the request. For cross-service or durable delivery, publish to a real message broker (e.g. RabbitMQ, Azure Service Bus) instead.
// In-process reactions -> MediatR notifications
// Cross-service / durable -> message brokerTesting Notifications
You can verify a handler publishes the right event by passing a mock IPublisher and asserting it was called.
var publisher = new Mock<IPublisher>();
var handler = new CreateOrderHandler(db, publisher.Object);
await handler.Handle(new CreateOrderCommand(1, []), default);
publisher.Verify(p => p.Publish(
It.IsAny<OrderPlacedNotification>(), default), Times.Once);Quick Check
Confirm the notification model.
Recap
You learned notifications and events:
INotificationis a broadcast with zero or moreINotificationHandlers.- Publish with
IPublisher.Publishto invoke all subscribers. - Notifications decouple side effects and follow the open/closed principle.
- They are in-process; use a broker for cross-service or durable events.
That completes the CQRS and MediatR course.
Frequently asked questions
Is the “Notifications and Events” lesson free?
Yes — the full text of “Notifications and Events” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.
What will I learn in “Notifications and Events”?
Publish domain events with MediatR. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Notifications and Events” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- CQRS Concepts
- Commands and Handlers with MediatR
- Pipeline Behaviors
- Notifications and Events