Przedsprzedaż kursu SSJ: Super Senior Java rusza w listopadzie. Jeśli chcesz mieć najtaniej, zapisz się już teraz!Zapisz się →
📦 Ten wpis pochodzi z 2024 roku i jest częścią archiwum. Treść może być nieaktualna. ← Wróć do archiwum
blog

Czy istnieje coś takiego jak zdarzenie modułowe?

#100commitów#zdarzenie#rozterki#optimistic locking#programowanie#architektura aplikacji

Wracając do mojego poprzedniego wpisu, naszła mnie pewna refleksja. Napisałem kiedyś czym różni się zdarzenie domenowe od integracyjnego opierając się o wpisy znalezione na blogach w Internecie. Teraz mam wrażenie, że jest jeszcze jeden typ. Nie wiem czy jest on gdziekolwiek opisany w literaturze. Chodzi mi o zdarzenia wewnątrzmodułowe, które “odkryłem” na bazie swoich doświadczeń. Wróćmy do przykładu z artykułu “Blokowanie optymistyczne a zdarzenia”.

public Try<Occupation> occupy(
    BeneficiaryId beneficiaryId,
    ParkingSpotId parkingSpotId,
    SpotUnits spotUnits
) {
  return Try.of(() -> {
    if (!beneficiaryRepository.isPresent(beneficiaryId)) {
      throw new IllegalStateException("cannot find beneficiary with id " + beneficiaryId);
    }
    ParkingSpot parkingSpot = findBy(parkingSpotId);

    log.debug("occupying parking spot with id {} by beneficiary with id {}", parkingSpot.getParkingSpotId(), beneficiaryId);
    var result = parkingSpot.occupy(beneficiaryId, spotUnits);

    return Match(result).of(
        Case($Right($()), event -> {
          parkingSpotRepository.publish(event);
          return event.occupied().occupation();
        }),
        Case($Left($()), exception -> {
          throw exception;
        })
    );
  }).onFailure(exception -> log.error("cannot occupy parking spot, reason: {}", exception.getMessage()));
}
@Getter
@AllArgsConstructor
public class ParkingSpot {

  // ...

  public Either<RuntimeException, ParkingSpotOccupiedEvents> occupy(
      BeneficiaryId beneficiaryId,
      SpotUnits spotUnits
  ) {
    if (outOfUse) {
      return Either.left(new IllegalArgumentException("out of order"));
    }
    if (exceedsAllowedSpace(spotUnits)) {
      return Either.left(new IllegalArgumentException("not enough space"));
    }
    return Either.right(
        ParkingSpotOccupiedEvents.events(
            new ParkingSpotOccupied(
                parkingSpotId,
                Occupation.newOne(beneficiaryId, parkingSpotId, spotUnits),
                version)));
  }

}

Zdarzenie publikowane przez linijkę parkingSpotRepository.publish(event) zawiera w sobie wcześniej wspomnianą wersję na podstawie której dokonano walidacji. Nie chcę, aby ktokolwiek dowiedział się o takiej wewnętrznej strukturze zdarzenia. Ta struktura jest potrzebna w wybranym module, aby zapisać tylko te zmiany, które są istotne z punktu widzenia danej funkcjonalności biznesowej oraz, aby móc zaimplementować Optimistic Locking.

Może się jednak zdarzyć, że inny moduł chciałby wiedzieć, że dane miejsce postojowe zostało zajęte. Wtedy do gry niezbędne będzie dołączenie zdarzenia międzymodułowego.

@Override
public void publish(ParkingSpotEvent event) {
  if (event instanceof ParkingSpotEvent.ParkingSpotOccupied occupied) {
  	//... increment ParkingSpotEntity version (optimistic locking)

    InMemoryOccupationRepository.DATABASE.add(new OccupationEntity(
        occupied.occupation().getOccupationId().getValue(),
        occupied.occupation().getBeneficiaryId().getValue(),
        occupied.occupation().getParkingSpotId().getValue(),
        occupied.occupation().getSpotUnits().getValue()));

    eventPublisher.publish(new ParkingSpotOccupied(
    	occupied.occupation().getBeneficiaryId(),
        occupied.occupation().getParkingSpotId(),
        occupied.occupation().getSpotUnits()))
  }
  //... else ifs
}

Powyżej mamy implementację tego przypadku dla bazy in memory. Schemat jest taki:

  • Podbijamy wersję encji ParkingSpotEntity o ile jest ona taka sama w zdarzeniu jak i bazie danych
  • Dodajemy nową “zajętość” miejsca postojowego
  • Publikujemy zdarzenie międzymodułowe/integracyjne

Co o tym myślisz? Ma to sens? Mocno się nad tym zastanawiałem i nie potrafiłem z tego inaczej wybrnąć. Nie wiem czy to nie nadmierna komplikacja i da się zrobić to prościej.

Znajdź mnie na