Effects Composition¶
The EmailModule is an [EffectsModule(typeof(IEmailService))] that generates Eff<RT, T> effect methods for every IEmailService method. This enables functional composition with built-in OpenTelemetry tracing.
Capability Interface¶
The generator produces:
Any runtime implementing IHasEmailService can resolve the email service at execution time.
Effect Methods¶
Generated as nested static members on EmailModule:
public static partial class EmailModule
{
public static partial class Email
{
public static Eff<RT, EmailResult> SendAsync<RT>(EmailMessage message)
where RT : IHasEmailService => ...
public static Eff<RT, IReadOnlyList<EmailResult>> SendBatchAsync<RT>(
IReadOnlyList<EmailMessage> messages)
where RT : IHasEmailService => ...
}
}
Composing with [Runtime] and [Uses]¶
Wire into your runtime:
[Runtime]
public sealed partial class AppRuntime;
// EmailModule is auto-discovered if in the same assembly
// Use [Uses(typeof(EmailModule))] for external assemblies
Effect Pipeline Examples¶
Send a single email¶
var sendWelcome =
from result in EmailModule.Email.SendAsync<AppRuntime>(new EmailMessage
{
From = new EmailAddress("noreply@example.com"),
To = [new EmailAddress(user.Email)],
Subject = "Welcome!",
HtmlBody = $"<h1>Welcome, {user.Name}</h1>"
})
select result;
Send with error handling¶
var sendWithRetry =
from result in EmailModule.Email.SendAsync<AppRuntime>(message)
from _ in result.Status == EmailStatus.Rejected
? FailEff<AppRuntime, Unit>(Error.New(502, $"Email rejected: {result.ErrorMessage}"))
: unitEff
select result;
Compose with other effects¶
// Send order confirmation + update audit log
var confirmOrder =
from order in AppStore.Orders.GetById<AppRuntime>(orderId)
.Require(Error.New(404, "Order not found"))
from emailResult in EmailModule.Email.SendAsync<AppRuntime>(new EmailMessage
{
From = new EmailAddress("orders@example.com"),
To = [new EmailAddress(order.CustomerEmail)],
Subject = $"Order #{order.Id} Confirmed",
HtmlBody = RenderConfirmation(order),
Tags = new Dictionary<string, string> { ["order_id"] = order.Id.ToString() }
})
from _ in AuditModule.Audit.LogAsync<AppRuntime>(
"Order", order.Id.ToString(), "confirmation_sent")
select emailResult;
Batch send with tracking¶
var sendCampaign =
from recipients in AppStore.Subscribers.GetActive<AppRuntime>()
from results in EmailModule.Email.SendBatchAsync<AppRuntime>(
recipients.Select(r => new EmailMessage
{
From = new EmailAddress("campaign@example.com"),
To = [new EmailAddress(r.Email)],
Subject = campaign.Subject,
HtmlBody = campaign.Body,
Tags = new Dictionary<string, string> { ["campaign_id"] = campaign.Id.ToString() }
}).ToList())
select results;