Eventos de Domínio e Como Tornar o Desacoplamento Algo Real
Tags: ddd, architecture, java
Published: 2020-02-01 00:09:09

Evento de domínio é uma tática abordada pelo Domain Driven Design(DDD) desde sempre, mas percebo que muitos desenvolvedores subestimam a capacidade que o evento de domínio tem de tornar o seu software menos acoplado, simples e de responsabilidades bem definidas.

Alguns desenvolvedores chegam a dizer que é uma forma de trabalhar altamente complexa de ser implementada e que torna o software moroso para se trabalhar.

Hoje eu venho mostrar como tudo isso pode ser implementado de maneira simples e cumprir as promessas acima.

Um evento de domínio é a abstração de um acontecimento que tenha ocorrido. Ele sempre vai ser gerado e notificado através de um notificador de eventos que por sua vez vai "entregar" esse evento a os interessados em ouvi-lo, ou seja os manipuladores de eventos(listeners).

Antes de continuar vou demonstrar cada um desses padrões mencionados para que você comece a ver como tudo isso é implementado e como pode ser usado na sua aplicação. Utilizaremos Java 8 e Spring Boot 2.2.2, o código completo esta no meu [GitHub](github.com/diegopaniago/eventos-de-dominio/).

Vou usar como exemplo fictício um contexto responsável por gerir e persistir cadastros de pessoas.

Esta é abstração de um evento de domínio onde irá carregar os dados que fizerem sentido para você. Recomendo que o momento da ocorrência sempre esteja presente nesta abstração para que consiga organizar cronologicamente os acontecimentos principalmente se eles forem parar em uma mensageiria.

```java
public abstract class EventoDeDominio extends ApplicationEvent {

    private LocalDateTime data;

    public EventoDeDominio(Object source, LocalDateTime data) {
        super(source);
        this.data = data;
    }

    public LocalDateTime getData() {
        return data;
    }
}
```

Então o evento em si fica assim.

```java
public class NomeDaPessoaAlterado extends EventoDeDominio {

    private String nomeAntigo;
    private String nomeAtual;

    public NomeDaPessoaAlterado(Object source, LocalDateTime data, String nomeAntigo, String nomeAtual) {
        super(source, data);
        this.nomeAntigo = nomeAntigo;
        this.nomeAtual = nomeAtual;
    }

    public String getNomeAntigo() {
        return nomeAntigo;
    }

    public String getNomeAtual() {
        return nomeAtual;
    }
}
```

Como estou usando o Spring Boot ele já me entrega **pronta** uma implementação de um notificador de eventos que é a classe ApplicationEventPublisher. É principalmente quando falamos deste notificador onde começam as implementações complexas e muitas vezes com um pattern mais complexo que o outro sem necessidade. Usando este notificador na minha camada de aplicação, eu executo a ação de alterar uma pessoa, e se bem sucedida notifico o evento NomeDaPessoaAlterado.

```java
@Service
public class AtualizaPessoa {

    private PessoaRepositorio pessoaRepositorio;
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    public AtualizaPessoa(PessoaRepositorio pessoaRepositorio, ApplicationEventPublisher applicationEventPublisher) {
        this.pessoaRepositorio = pessoaRepositorio;
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void atualizarNome(PessoaDto pessoaDto) throws Exception {
        Pessoa pessoa = pessoaRepositorio.findById(pessoaDto.id).get();
        NomeDaPessoaAlterado nomeDaPessoaAlterado = new NomeDaPessoaAlterado(this, LocalDateTime.now(),
            pessoa.getNome(),
            pessoaDto.nome);
        pessoa.alterarNome(pessoaDto.nome);
        pessoaRepositorio.save(pessoa);
        applicationEventPublisher.publishEvent(nomeDaPessoaAlterado);
    }
}
```

A partir do momento que temos um evento notificado precisamos apenas criar o(s) manipulador(es) interessados neste evento na nossa camada de infra para que seja possível extrair as regras de negócio que não fazem parte da responsabilidade do serviço de alterar.

```java
@Component
public class ManipuladorDeNomeDaPessoaAlterado implements ApplicationListener<NomeDaPessoaAlterado> {
    @Override
    public void onApplicationEvent(NomeDaPessoaAlterado nomeDaPessoaAlterado) {
        System.out.println("Evento Pessoa alterada");
        System.out.println(nomeDaPessoaAlterado.getData().toString());
        System.out.println(nomeDaPessoaAlterado.getNomeAntigo());
        System.out.println(nomeDaPessoaAlterado.getNomeAtual());
    }
}
```

A partir desse ponto eu desacoplo outras regras de negócio que cercam a ação de alterar uma Pessoa da responsabilidade única e exclusiva do serviço AtualizaPessoa de realizar o update da entidade Pessoa no banco.

Eventos de domínio servem para tornar o seu código, infra e contexto simples e desacoplados. Eles permitem que complexidades referentes a **outras regras de negócio** sejam implementadas em um outro momento, tornando o código da ação que esta sendo executada de **responsabilidade única** e bem **delimitada**.

Como no exemplo acima, o comando do momento é apenas alterar os dados da Entidade Pessoa, se minha regra de negócio diz que devo por exemplo salvar o log do acontecimento ou atualizar outros dados de outras Entidades no momento que a alteração ocorrer, essas complexidades podem ser implementadas separadamente e consequentemente você vai ganhar desacoplamento, facilidade de testar, e melhor controle transacional(limite transacional).

Essa tática permite que sua regra de negócio possa evoluir indefinidamente sem que a mera ação de alterar os dados de um cadastro, como no exemplo, exploda o tamanho do seu serviço de alteração, se ele é de alteração ele deve estritamente, unicamente, haja o que houver alterar o cadastro e nada mais.

Para finalizar, outro benefício dessa abordagem é que ela permite começar a extrair um contexto de um sistema monolítico de forma gradual, de tal forma que aos poucos as regras de negócio referentes ao contexto que deseja extrair possam começar a serem executados em outro local.