¿Por qué tu código solo se despierta cuando lo llaman?
\nImagina que eres un controlador en un enorme aeropuerto. Los aviones llegan, los pasajeros se apresuran, los camiones de combustible maniobran. Si tuvieras que ir personalmente a cada piloto y preguntarle: «Bueno, ¿ya aterrizaste? ¿Y ahora? ¿Y ahora?» — te volverías loco. En cambio, dices: «Cuando aterrices, llama». Y el sistema funciona perfectamente.
\nEsto es la arquitectura basada en eventos en C#. En lugar de sondear objetos constantemente (pull), nos suscribimos a eventos (push). El código se vuelve reactivo, escalable y, lo más importante, vivo. En este artículo, analizaremos cómo construir estos sistemas en C# desde cero, utilizando los mecanismos integrados del lenguaje y patrones avanzados.
\n\n¿Qué es la arquitectura basada en eventos? No es teoría aburrida
\nLa arquitectura basada en eventos (event-driven) es un enfoque en el que el flujo de ejecución del programa está determinado por eventos: clics, mensajes de colas, cambios de datos o señales externas. En C#, este concepto está integrado directamente en el lenguaje a través de delegados (delegates) y eventos (events).
\nEl principio principal es el acoplamiento débil (loose coupling). El emisor del evento (publicador) no sabe nada sobre los receptores (suscriptores). Simplemente grita: «¡Algo importante ha sucedido!» — y quién y cómo reaccionará se decide dinámicamente.
\n¿Por qué esto es genial para proyectos reales?
\n- \n
- Escalabilidad: Agregar un nuevo suscriptor es como suscribirlo a un boletín. El código del publicador no cambia. \n
- Capacidad de prueba: Puedes probar los suscriptores de forma aislada, proporcionándoles eventos falsos. \n
- Reactividad: El sistema reacciona por sí mismo a los cambios, sin desperdiciar recursos en sondeos vacíos. \n
Mecanismo básico: Delegados y eventos en C#
\nAntes de construir la arquitectura, analicemos los fundamentos. En C#, un evento es simplemente un delegado especializado con el modificador event. Un delegado es un tipo que almacena una referencia a un método (o varios métodos).
Escribimos nuestro primer código basado en eventos
\nImagina que estamos escribiendo un sistema de hogar inteligente. Un sensor de movimiento envía una señal cuando alguien entra.
\n\npublic class MotionSensor\n{\n // 1. Declaramos el delegado (tipo de evento)\n public delegate void MotionDetectedHandler(object sender, MotionEventArgs e);\n \n // 2. Declaramos el evento\n public event MotionDetectedHandler OnMotionDetected;\n\n public void Detect(string location)\n {\n Console.WriteLine($"[Sensor] ¡Movimiento en {location}!");\n \n // 3. Invocamos el evento (solo si hay suscriptores)\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// Suscriptor — la lámpara\npublic class SmartLight\n{\n public void OnMotion(object sender, MotionEventArgs e)\n {\n if (e.Location == "Sala de estar")\n Console.WriteLine($"[Lámpara] ¡Enciendo la luz en {e.Location}!");\n else\n Console.WriteLine($"[Lámpara] Ignoro {e.Location}.");\n }\n}\n\n// Uso\nvar sensor = new MotionSensor();\nvar light = new SmartLight();\n\n// Suscribimos la lámpara al evento del sensor\nsensor.OnMotionDetected += light.OnMotion;\n\nsensor.Detect("Sala de estar"); // La lámpara se activa\nsensor.Detect("Cocina"); // "Ignoro Cocina."\n\nImportante: El uso de ?.Invoke es una forma segura para subprocesos de invocar un evento. Sin la comprobación de null, corres el riesgo de obtener una NullReferenceException si no hay suscriptores.
Nivel avanzado: EventBus y acoplamiento débil
\nLas suscripciones directas están bien para