Event-Driven Architecture in C#: How to Manage Events Like a Pro

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Does Your Code Only Wake Up When Called?

\n

Imagine you are a dispatcher at a huge airport. Planes are arriving, passengers are rushing, fuel trucks are maneuvering. If you personally went to every pilot and asked, "Well, have you landed? How about now? And now?" — you would go crazy. Instead, you say, "When you land — call me." And the system works perfectly.

\n

This is event-driven architecture in C#. Instead of constantly polling objects (pull), we subscribe to events (push). The code becomes reactive, scalable, and, most importantly, alive. In this article, we will break down how to build such systems in C# from scratch, using the language's built-in mechanisms and advanced patterns.

\n\n

What is Event-Driven Architecture? Not Boring Theory

\n

Event-driven architecture is an approach where the program's execution flow is determined by events: clicks, messages from queues, data changes, or external signals. In C#, this concept is built directly into the language through delegates and events.

\n

The main principle is loose coupling. The event sender (publisher) knows nothing about the receivers (subscribers). It simply shouts, "Something important happened!" — and who reacts to it and how is decided dynamically.

\n

Why is this great for real projects?

\n
    \n
  • Scalability: Adding a new subscriber is like signing them up for a newsletter. The publisher's code doesn't change.
  • \n
  • Testability: You can test subscribers in isolation by feeding them fake events.
  • \n
  • Reactivity: The system reacts to changes on its own, without wasting resources on empty polls.
  • \n
\n\n

Basic Mechanism: Delegates and Events in C#

\n

Before building the architecture, let's understand the foundation. In C#, an event is simply a specialized delegate with the event modifier. A delegate is a type that holds a reference to a method (or multiple methods).

\n\n

Writing Your First Event-Driven Code

\n

Imagine we are writing a smart home system. A motion sensor sends a signal when someone enters.

\n\n
public class MotionSensor\n{\n    // 1. Declare the delegate (event type)\n    public delegate void MotionDetectedHandler(object sender, MotionEventArgs e);\n    \n    // 2. Declare the event\n    public event MotionDetectedHandler OnMotionDetected;\n\n    public void Detect(string location)\n    {\n        Console.WriteLine($"[Sensor] Motion detected in {location}!");\n        \n        // 3. Raise the event (only if there are subscribers)\n        OnMotionDetected?.Invoke(this, new MotionEventArgs(location));\n    }\n}\n\npublic class MotionEventArgs : EventArgs\n{\n    public string Location { get; }\n    public DateTime Timestamp { get; } = DateTime.UtcNow;\n\n    public MotionEventArgs(string location)\n    {\n        Location = location;\n    }\n}\n\n// Subscriber — a light\npublic class SmartLight\n{\n    public void OnMotion(object sender, MotionEventArgs e)\n    {\n        if (e.Location == "Living Room")\n            Console.WriteLine($"[Light] Turning on the light in {e.Location}!");\n        else\n            Console.WriteLine($"[Light] Ignoring {e.Location}.");\n    }\n}\n\n// Usage\nvar sensor = new MotionSensor();\nvar light = new SmartLight();\n\n// Subscribe the light to the sensor's event\nsensor.OnMotionDetected += light.OnMotion;\n\nsensor.Detect("Living Room"); // The light will activate\nsensor.Detect("Kitchen");    // "Ignoring Kitchen."
\n\n

Important: Using ?.Invoke is a thread-safe way to raise an event. Without the null check, you risk a NullReferenceException if there are no subscribers.

\n\n

Advanced Level: EventBus and Loose Coupling

\n

Direct subscriptions are fine for

Blogs

Book Recommendations