Zastanawiałem się czy poniższy kod to dobra praktyka czy też nie. Niemniej, w obecnym projekcie się sprawdza. Na razie. Pomyślałem więc, że skoro się “na razie” sprawdza to warto się nią podzielić. I o to ona!

1
2
3
4
5
6
7
public interface LogSomethingEvent extends Event {

  MainId mainId();

  String fullMessage();

}

Oczywiście kod został zobfuskowany, ale główna idea pozostaje niezmenna. Stworzyliśmy w zespole interfejs, który jest takim znacznikiem eventowym zdefiniowanym przez moduł go obsługujący (jego publiczne API). Wygląda więc na komendę, a to już budzi niepokój czy to faktycznie jest “prawidłowe” rozwiązanie. Jednak dzięki niemu wystarczy, że inny, konkretny event zaimplementuje powyższy interfejs i log w naszym audycie gotowy!

1
2
3
4
5
6
7
8
9
10
11
public record TaskReviewedEvent(
    MainId mainId,
    String taskName
) implements DomainEvent, LogSomethingEvent {

  @Override
  public String fullMessage() {
    return "\"" + taskName + "\" has been reviewed";
  }

}
1
2
3
4
5
6
7
8
9
@EventListener
@Transactional
public void handle(LogSomethingEvent event) {
  MainId mainId = event.mainId();

  Log log = logFactory.create(mainId, event.fullMessage());

  logRepository.save(log);
}

I to tyle. Handler dla eventu jest naprawdę stabilny. Od dłuższego czasu nie ulega żadnym zmianom. A znacznik jest zaimplementowany już przez kilkanaście klas.

Jedyne co budzi nasz niepokój to dane, które trzeba przekazać do eventu, aby wygenerować odpowiedni log. W powyższym przypadku jest to taskName. Z punktu widzenia domeny jest on kompletnie nie potrzebną informacją - oczywiście poza tworzeniem historii audytowej. Jednak nie jest to aż taki duży narzut powodujący straszne problemy. Da się z tym żyć.

Daj znać co o sądzisz o powyższym rozwiązaniu. Czy warto odchodzić od dobrych praktyk (event to nie komenda) i być pragmatycznym? Czy też nie? Być może widzisz w powyższym rozwiązaniu jakieś logiczne dziury, nad którymi trzeba się zastanowić.