DEV Community

elshwilahmed
elshwilahmed

Posted on

Building a Decoupled Order Notification System in C# Using the Observer Pattern

The Solution: The Observer Pattern
To decouple our system, we can use the Observer Pattern. In C#, this is implemented using delegate and event.

The OrderService will act as the Publisher. It doesn't know who will receive the notification; it just announces that "An order has been placed".
The notification services EmailService and SMSService will act as Subscribers.

  1. The Publisher (OrderService) Here, we define an Action event. Notice how we use a Predicate to evaluate business rules (checking if the order price meets a condition) before firing the event using Invoke function.
public class OrderService
{
    public event Action<OrderService> OrderPlaced;

    public string Name { get; set; }
    public double Price { get; set; }

    public OrderService(string name, double price)
    {
        Name = name;
        Price = price;
    }

    public void ConfirmOrder(Predicate<double> condition)
    {
        Console.WriteLine($"Order '{Name}' has been successfully placed!");

        // Evaluate condition before firing the event
        if (condition(Price))
        {
            OrderPlaced?.Invoke(this);
        }
        Console.WriteLine("-------------------------------------------------");
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. The Subscribers (Notification Services) These classes are independent of the OrderService. They just need a method whose signature matches the Action delegate.
public class SMSService
{
    public void Notification(OrderService order) 
    {
        Console.WriteLine($"[SMS] Sending text message: Your order '{order.Name}' is confirmed!");
    }
}

public class EmailService
{
    public void Notification(OrderService order)
    {
        Console.WriteLine($"[Email] Sending email: Thank you for your order '{order.Name}' is confirmed!");
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Wiring It All Together In my code, I subscribe the services to the OrderPlaced event using the += operator. If I want to remove a service later, we simply use -=.
class Program
{
    static void Main(string[] args)
    {
        OrderService order = new("Exercise Straps", 228);

        SMSService sendSMS = new();
        EmailService sendEmail = new();

        // Subscribe to the event
        order.OrderPlaced += sendEmail.Notification;
        order.OrderPlaced += sendSMS.Notification;

        // Confirm order with a condition (price > 200)
        order.ConfirmOrder(x => x > 200);
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion: Why This Matters

By using the Observer Pattern with C# delegates and events, I transformed a tightly coupled method into a scalable and flexible system.

If i ever need to add a WhatsAppNotificationService, I simply create the class and subscribe it to the OrderPlaced event without touching the main OrderService at all. This is the importance of the Open/Closed Principle!

You can check out the complete, working code for this project on my GitHub repository here:
https://github.com/elshwilahmed/CAT-Task6-Order-Notification-System/tree/main

How do you handle event-driven architectures in your .NET projects? Let me know in the comments!

Top comments (0)