package me.belkacemi.iotalarm.infrastructure.persistence; import me.belkacemi.iotalarm.application.query.port.AlarmReadModelPort; import me.belkacemi.iotalarm.application.query.view.AlarmStatusView; import me.belkacemi.iotalarm.application.query.view.AlertView; import me.belkacemi.iotalarm.domain.model.AlarmPolicy; import me.belkacemi.iotalarm.domain.model.AlarmState; import me.belkacemi.iotalarm.infrastructure.persistence.jpa.AlarmStateJpaRepository; import me.belkacemi.iotalarm.infrastructure.persistence.jpa.AlertJpaRepository; import me.belkacemi.iotalarm.infrastructure.persistence.mapper.AlertMapper; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Component; import java.time.Clock; import java.util.List; import java.util.Optional; import java.util.UUID; /** * ADAPTER SORTANT du cote LECTURE : implementation du {@link AlarmReadModelPort}. * *

Il ne charge jamais l'agregat. Il assemble une vue a partir de requetes * ciblees - c'est la raison d'etre du CQRS : afficher un compteur d'alertes non * acquittees via un {@code COUNT(*)} plutot qu'en rehydratant un objet metier * complet dont on n'utiliserait aucune methode.

*/ @Component public class AlarmReadModelAdapter implements AlarmReadModelPort { private final AlertJpaRepository alertRepository; private final AlarmStateJpaRepository alarmStateRepository; private final AlarmPolicy policy; private final Clock clock; public AlarmReadModelAdapter(AlertJpaRepository alertRepository, AlarmStateJpaRepository alarmStateRepository, AlarmPolicy policy, Clock clock) { this.alertRepository = alertRepository; this.alarmStateRepository = alarmStateRepository; this.policy = policy; this.clock = clock; } @Override public Optional alarmStatus(String zoneId) { long unacknowledged = alertRepository.countByZoneIdAndAcknowledgedFalse(zoneId); AlertView lastAlert = alertRepository.findFirstByZoneIdOrderByDetectedAtDesc(zoneId) .map(AlertMapper::toView) .orElse(null); return Optional.of(alarmStateRepository.findById(zoneId) .map(entity -> new AlarmStatusView( entity.getZoneId(), entity.getState().name(), entity.getStateChangedAt(), policy.exitDelay().toSeconds(), entity.getLastAlertAt(), unacknowledged, lastAlert)) // Base neuve : l'alarme n'a simplement jamais ete armee. La // console doit afficher DESARMEE, pas une erreur 404. .orElseGet(() -> new AlarmStatusView( zoneId, AlarmState.DISARMED.name(), clock.instant(), policy.exitDelay().toSeconds(), null, unacknowledged, lastAlert))); } @Override public List recentAlerts(String zoneId, int limit) { return alertRepository .findByZoneIdOrderByDetectedAtDesc(zoneId, PageRequest.of(0, limit)) .stream() .map(AlertMapper::toView) .toList(); } @Override public Optional findAlert(UUID alertId) { return alertRepository.findById(alertId).map(AlertMapper::toView); } }