ASP.NET Hosting

Notification Services Abstract Factory Pattern in C# 14

Why Send Notifications Using Abstract Factory?

Notifications from modern apps frequently need to be sent through email, SMS, and push notifications. Additionally, depending on factors like geography, cost, or dependability, any of those channels may have a number of third-party suppliers. For instance, you may use Amazon SES for transactional messages, Mailgun for development, and SendGrid for production emails. Your code becomes brittle and tightly connected if it instantiates a provider class directly (for example, new SendGridEmailService()).

The development of related services, such EmailService, SmsService, and PushNotificationService, can be neatly encapsulated using the Abstract Factory paradigm. Without altering any client logic, it enables you to switch in new provider families (such as Twilio, Firebase, and SNS).

Real-World Structure

Let’s say your app needs to,

  • Send emails (confirmation, password reset)
  • Send SMS (2FA, alerts)
  • Send push notifications (mobile or web)

You want to support multiple notification providers.

  • SendGrid for email
  • Twilio for SMS
  • Firebase Cloud Messaging (FCM) for push notifications

Let’s design,

  • Abstract interfaces for each service
  • An abstract factory to create them
  • Concrete factory implementations for Twilio/SendGrid/FCM
  • Client code that doesn’t care which backend it’s using

Step 1. Define Service Interfaces.

Each service has its own responsibility. We start with abstract interfaces.

public interface IEmailService
{
    void SendEmail(string to, string subject, string body);
}

public interface ISmsService
{
    void SendSms(string phoneNumber, string message);
}

public interface IPushNotificationService
{
    void SendPush(string deviceToken, string title, string message);
}

Each of these interfaces defines a simple contract. Whether you’re using Twilio, SendGrid, Firebase, or mock implementations, they all follow these contracts so the rest of the app doesn’t need to change.

Step 2. Define the Abstract Factory.

Now, we define the abstract factory interface that creates the related family of notification services.

public interface INotificationFactory
{
    IEmailService CreateEmailService();
    ISmsService CreateSmsService();
    IPushNotificationService CreatePushService();
}

This factory is the core abstraction. It defines how to get the right implementation of each service. Depending on which factory you inject—TwilioFactory, FirebaseFactory, etc.—you’ll get the corresponding set of services.

Step 3. SendGrid + Twilio + Firebase Implementation.

Let’s implement a full provider set. We’ll simulate the behavior with Console.WriteLine, but these would wrap real SDKs in a production app.

SendGrid Email Service

public class SendGridEmailService : IEmailService
{
    public void SendEmail(string to, string subject, string body)
    {
        Console.WriteLine($"[SendGrid] Sending email to {to}: {subject} - {body}");
    }
}

Twilio SMS Service

public class TwilioSmsService : ISmsService
{
    public void SendSms(string phoneNumber, string message)
    {
        Console.WriteLine($"[Twilio] Sending SMS to {phoneNumber}: {message}");
    }
}

Firebase Push Notification

public class FirebasePushService : IPushNotificationService
{
    public void SendPush(string deviceToken, string title, string message)
    {
        Console.WriteLine($"[Firebase] Push to {deviceToken}: {title} - {message}");
    }
}

These classes are isolated, easy to test individually, and swappable. Each can be extended with real API logic and credentials securely loaded from configuration.

Step 4. The Provider Factory.

Now let’s create a factory that returns the above services.

public class CloudNotificationFactory : INotificationFactory
{
    public IEmailService CreateEmailService() => new SendGridEmailService();
    public ISmsService CreateSmsService() => new TwilioSmsService();
    public IPushNotificationService CreatePushService() => new FirebasePushService();
}

This factory encapsulates the full “cloud-based” provider set. You could later create TestNotificationFactory, EnterpriseNotificationFactory, or even NullNotificationFactory for silent mode.

Step 5. Client Code.

Now we create a service that uses the notification factory.

public class NotificationManager
{
    private readonly IEmailService _email;
    private readonly ISmsService _sms;
    private readonly IPushNotificationService _push;

    public NotificationManager(INotificationFactory factory)
    {
        _email = factory.CreateEmailService();
        _sms = factory.CreateSmsService();
        _push = factory.CreatePushService();
    }

    public void SendUserNotifications()
    {
        _email.SendEmail("user@example.com", "Welcome!", "Thanks for signing up!");
        _sms.SendSms("+15551234567", "Your 2FA code is 123456");
        _push.SendPush("devicetoken123", "New Alert", "Check out the latest update.");
    }
}

This manager handles notification logic. It can be reused across environments without ever knowing what service is used behind the scenes.

Program Entry Point

class Program
{
    static void Main()
    {
        // Select provider factory
        INotificationFactory factory = new CloudNotificationFactory();

        var notifier = new NotificationManager(factory);
        notifier.SendUserNotifications();
    }
}

This setup means you can swap notification ecosystems by changing one line. In real apps, this could be injected via ASP.NET Core’s dependency injection system.

Real-World Benefits

The Abstract Factory approach here gives you,

  • Pluggability: Switch providers (e.g., AWS vs Azure) without rewriting logic
  • Testability: Mock services for unit tests (e.g., TestNotificationFactory)
  • Extensibility: Add Slack or Discord in future without changing the NotificationManager
  • Clean architecture: No service knows about concrete implementations

This also scales really well when you introduce priority-based delivery, multi-region fallback, or tiered plans (e.g., premium users get SMS + push).

Bonus: Test Notification Factory

public class TestNotificationFactory : INotificationFactory
{
    public IEmailService CreateEmailService() => new FakeEmailService();
    public ISmsService CreateSmsService() => new FakeSmsService();
    public IPushNotificationService CreatePushService() => new FakePushService();
}

public class FakeEmailService : IEmailService
{
    public void SendEmail(string to, string subject, string body) =>
        Console.WriteLine($"[TEST] Email: {to}, {subject}");
}
public class FakeSmsService : ISmsService
{
    public void SendSms(string number, string message) =>
        Console.WriteLine($"[TEST] SMS: {number}, {message}");
}
public class FakePushService : IPushNotificationService
{
    public void SendPush(string token, string title, string message) =>
        Console.WriteLine($"[TEST] Push: {token}, {title}");
}

Use this in dev or CI pipelines to validate notification logic without sending actual messages.

ASP.NET Core 8.0.11 Hosting Recommendation

HostForLIFE.eu
HostForLIFE.eu is a popular recommendation that offers various hosting choices. Starting from shared hosting to dedicated servers, you will find options fit for beginners and popular websites. It offers various hosting choices if you want to scale up. Also, you get flexible billing plans where you can choose to purchase a subscription even for one or six months.