- Architecture Overview - High level architecture and component design
- API Versioning and APIM - API versioning strategy and APIM routing
- Domain Setup - Guide for adding new sending domains
- Development Workflows - Branch strategy, CI/CD triggers, and deployment flows
Platform Notifications is a centralised .NET 9 email notification service backed by Azure Communication Services that provides domain-granular sending across six custom domains (xtremeidiots.com, molyneux.io, molyneux.me, molyneux.dev, geo-location.net, craftpledge.org). The API is fronted by an APIM Consumption instance with Entra ID app role authorization, and messages flow through a Service Bus queue for resilient delivery with Polly retries and dead-letter handling. Infrastructure is managed by Terraform and deployed via GitHub Actions workflows above.
| Package | Description |
|---|---|
MX.Platform.Notifications.Abstractions.V1 |
Interfaces and DTOs for the Notifications API |
MX.Platform.Notifications.Api.Client.V1 |
Typed HTTP client with DI registration via AddNotificationsApiClient() |
MX.Platform.Notifications.Api.Client.Testing |
In-memory fakes and DTO factory helpers for consumer test projects |
// In your Program.cs or Startup.cs
services.AddNotificationsApiClient(options => options
.WithBaseUrl("https://apim-platform-notifications-prd-uksouth.azure-api.net/notifications")
.WithEntraIdAuthentication("api://{tenant-id}/platform-notifications-api-prd"));public class MyService(INotificationsApiClient notifications)
{
public async Task NotifyUser(string email, string name)
{
var request = new SendEmailRequestDto
{
SenderDomain = "contoso.com", // Must hold {domain}.email.sender app role
Subject = "Your report is ready",
HtmlBody = $"<p>Hi {name}, your report is ready to download.</p>",
PlainTextBody = $"Hi {name}, your report is ready to download.",
To = [new EmailRecipientDto { EmailAddress = email, DisplayName = name }]
};
var result = await notifications.Email.SendEmail(request);
if (!result.IsSuccess)
{
// result.StatusCode and result.Result.Errors contain failure details
throw new InvalidOperationException($"Email send failed: {result.StatusCode}");
}
// result.Result.Data.MessageId — unique tracking ID
// result.Result.Data.Status — "Queued" (delivery happens asynchronously)
}
}// Replace the real client in your test DI container
services.AddFakeNotificationsApiClient();
// Or use directly in unit tests
var fakeClient = new FakeNotificationsApiClient();
await fakeClient.Email.SendEmail(SendEmailRequestDtoFactory.CreateSendEmailRequest());
Assert.Single(fakeClient.EmailApi.SentEmails);Please read the contributing guidance; this is a learning and development project.
Please read the security guidance; I am always open to security feedback through email or opening an issue.