Skip to content

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

public interface IHasSmsService
{
    ISmsService SmsService { get; }
}

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;

Send MMS with media

var sendMms =
    from result in SmsModule.Sms.SendAsync<AppRuntime>(new SmsMessage
    {
        From = new PhoneNumber("+15551234567"),
        To = new PhoneNumber(recipient),
        Body = "Check out this photo!",
        MediaUrls = [new Uri("https://example.com/photo.jpg")]
    })
    select result;