NotificationDeliveryService.java
package com.ecommerce.notification.application.service;
import com.ecommerce.notification.application.model.NotificationModels.EmailDeliveryAttempt;
import com.ecommerce.notification.infrastructure.config.NotificationDeliveryProperties;
import com.ecommerce.notification.infrastructure.persistence.NotificationRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Service
public class NotificationDeliveryService {
private final NotificationRepository repository;
private final NotificationDeliveryProperties properties;
public NotificationDeliveryService(
NotificationRepository repository,
NotificationDeliveryProperties properties) {
this.repository = repository;
this.properties = properties;
}
@Transactional
public List<EmailDeliveryAttempt> claimDue() {
Instant now = repository.currentTime();
List<EmailDeliveryAttempt> due = repository.selectDueEmailDeliveriesForUpdate(
now,
properties.batchSize());
List<EmailDeliveryAttempt> claimed = new ArrayList<>();
for (EmailDeliveryAttempt candidate : due) {
if (repository.claimEmailDelivery(
candidate.deliveryId(),
candidate.attempt(),
properties.workerId(),
now.plus(properties.leaseDuration()),
now)) {
claimed.add(new EmailDeliveryAttempt(
candidate.deliveryId(),
candidate.attempt() + 1,
candidate.destination(),
candidate.providerMessageId(),
candidate.subject(),
candidate.content()));
}
}
return List.copyOf(claimed);
}
@Transactional
public boolean markSent(long deliveryId, int attempt) {
return repository.markDeliverySent(
deliveryId,
properties.workerId(),
attempt,
repository.currentTime());
}
@Transactional
public boolean markFailed(long deliveryId, int attempt, Exception exception) {
Instant now = repository.currentTime();
boolean terminal = attempt >= properties.maximumAttempts();
Instant nextAttemptAt = terminal
? now
: now.plus(properties.retryDelay().multipliedBy(Math.max(1, attempt)));
return repository.markDeliveryFailed(
deliveryId,
properties.workerId(),
attempt,
terminal ? "NEEDS_ATTENTION" : "RETRY",
nextAttemptAt,
conciseError(exception),
now);
}
private String conciseError(Exception exception) {
String detail = exception.getMessage() == null ? "" : exception.getMessage();
String message = exception.getClass().getSimpleName() + ": " + detail;
return message.length() <= 1000 ? message : message.substring(0, 1000);
}
}