Effects Composition¶
The SmsModule is an [EffectsModule(typeof(ISmsService))] that generates Eff<RT, T> effect methods for every ISmsService method. This enables functional composition with built-in OpenTelemetry tracing.
Capability Interface¶
Effect Methods¶
Generated as nested static members on SmsModule:
public static partial class SmsModule
{
public static partial class Sms
{
public static Eff<RT, SmsResult> SendAsync<RT>(SmsMessage message)
where RT : IHasSmsService => ...
public static Eff<RT, SmsResult> GetStatusAsync<RT>(SmsMessageId messageId)
where RT : IHasSmsService => ...
}
}
Composing with [Runtime]¶
[Runtime]
public sealed partial class AppRuntime;
// SmsModule is auto-discovered if in the same assembly
Effect Pipeline Examples¶
Send a verification code¶
var sendVerification =
from code in GenerateVerificationCode()
from result in SmsModule.Sms.SendAsync<AppRuntime>(new SmsMessage
{
From = new PhoneNumber("+15551234567"),
To = new PhoneNumber(user.Phone),
Body = $"Your verification code is {code}. Expires in 10 minutes."
})
from _ in Guard<AppRuntime>(result.Status == SmsStatus.Accepted,
Error.New(502, $"SMS send failed: {result.ErrorMessage}"))
select code;
Send + poll delivery status¶
var sendAndVerifyDelivery =
from sendResult in SmsModule.Sms.SendAsync<AppRuntime>(message)
from statusResult in SmsModule.Sms.GetStatusAsync<AppRuntime>(sendResult.Id)
select (sendResult, statusResult);
Compose with other effects¶
// Send appointment reminder + log to audit
var sendReminder =
from appointment in AppStore.Appointments.GetById<AppRuntime>(appointmentId)
.Require(Error.New(404, "Appointment not found"))
from smsResult in SmsModule.Sms.SendAsync<AppRuntime>(new SmsMessage
{
From = new PhoneNumber(config.SmsFromNumber),
To = new PhoneNumber(appointment.PatientPhone),
Body = $"Reminder: Appointment on {appointment.Date:MMM dd} at {appointment.Time:HH:mm}"
})
from _ in AuditModule.Audit.LogAsync<AppRuntime>(
"Appointment", appointmentId.ToString(), "reminder_sent")
select smsResult;