Por que seu código só acorda quando é chamado?
\nImagine que você é um controlador de tráfego aéreo em um enorme aeroporto. Aviões chegam, passageiros correm, caminhões de combustível manobram. Se você fosse pessoalmente a cada piloto e perguntasse: "E aí, pousou? E agora? E agora?" — você enlouqueceria. Em vez disso, você diz: "Quando pousar, me ligue." E o sistema funciona perfeitamente.
\nIsso é a arquitetura orientada a eventos em C#. Em vez de consultar objetos constantemente (pull), nos inscrevemos em eventos (push). O código se torna reativo, escalável e, o mais importante, vivo. Neste artigo, vamos explorar como construir esses sistemas em C# do zero, usando os mecanismos nativos da linguagem e padrões avançados.
\n\nO que é arquitetura orientada a eventos? Não é teoria chata
\nA arquitetura orientada a eventos (event-driven) é uma abordagem onde o fluxo de execução do programa é determinado por eventos: cliques, mensagens de filas, alterações de dados ou sinais externos. Em C#, esse conceito está incorporado diretamente na linguagem através de delegados (delegates) e eventos (events).
\nO princípio principal é o baixo acoplamento (loose coupling). O remetente do evento (publicador) não sabe nada sobre os receptores (assinantes). Ele apenas grita: "Algo importante aconteceu!" — e quem e como reagirá a isso é decidido dinamicamente.
\nPor que isso é legal para projetos reais?
\n- \n
- Escalabilidade: Adicionar um novo assinante é como inscrevê-lo em uma newsletter. O código do publicador não muda. \n
- Testabilidade: Você pode testar assinantes isoladamente, fornecendo eventos falsos. \n
- Reatividade: O sistema reage automaticamente às mudanças, sem desperdiçar recursos em consultas vazias. \n
Mecanismo básico: Delegados e eventos em C#
\nAntes de construir a arquitetura, vamos entender o fundamento. Em C#, um evento é simplesmente um delegado especializado com o modificador event. Um delegado é um tipo que armazena uma referência a um método (ou vários métodos).
Escrevendo o primeiro código orientado a eventos
\nImagine que estamos escrevendo um sistema de casa inteligente. Um sensor de movimento envia um sinal quando alguém entra.
\n\npublic class MotionSensor\n{\n // 1. Declaramos o delegado (tipo do evento)\n public delegate void MotionDetectedHandler(object sender, MotionEventArgs e);\n \n // 2. Declaramos o evento\n public event MotionDetectedHandler OnMotionDetected;\n\n public void Detect(string location)\n {\n Console.WriteLine($"[Sensor] Movimento em {location}!");\n \n // 3. Disparamos o evento (apenas se houver assinantes)\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// Assinante — lâmpada\npublic class SmartLight\n{\n public void OnMotion(object sender, MotionEventArgs e)\n {\n if (e.Location == "Sala de Estar")\n Console.WriteLine($"[Lâmpada] Ligando a luz na {e.Location}!");\n else\n Console.WriteLine($"[Lâmpada] Ignorando {e.Location}.");\n }\n}\n\n// Uso\nvar sensor = new MotionSensor();\nvar light = new SmartLight();\n\n// Inscrevemos a lâmpada no evento do sensor\nsensor.OnMotionDetected += light.OnMotion;\n\nsensor.Detect("Sala de Estar"); // A lâmpada será acionada\nsensor.Detect("Cozinha"); // "Ignorando Cozinha."\n\nImportante: Usar ?.Invoke é uma maneira segura para threads de disparar o evento. Sem a verificação de null, você corre o risco de obter um NullReferenceException se não houver assinantes.
Nível avançado: EventBus e baixo acoplamento
\nInscrições diretas são boas para