Tell sales about high-value orders

Java · Spring Boot · beginner · greenfield

Sales wants to call customers who place very large orders the same day, so orders over 10k EUR now post to their Slack channel after commit. Rather than calling Slack straight from the order code, alerts go through a small `AlertDispatcher`: channels register under a name, callers pick the channels and a severity, and a failing channel is logged without affecting the others. Adding email or PagerDuty later is one `register` call. Tested by placing a 12,500 EUR order locally against a test webhook.

Shop backend on Spring Boot 3.3 (Java 21). `OrderService.place` (existing) publishes `OrderPlaced(long orderId, BigDecimal total)` inside its `@Transactional` method, so a `@TransactionalEventListener` runs on the request thread after commit, and not at all on rollback. `SlackClient` (existing bean, package `com.acme.shop.slack`) has `void post(String webhookUrl, String text)` with 2-second connect and read timeouts and throws the unchecked `SlackException` on any failure (its message never contains the webhook URL); a synchronous post after commit is acceptable for this volume (a few such orders a day).

Requirements

Files touched

--- src/main/java/com/acme/shop/alerts/AlertDispatcher.java
+package com.acme.shop.alerts;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import com.acme.shop.slack.SlackClient;
+import com.acme.shop.slack.SlackException;
+
+/** Routes alerts to named channels, so new destinations can be added without touching callers. */
+@Component
+public class AlertDispatcher {
+
+    /** A destination for alerts. Register more with {@link #register}. */
+    @FunctionalInterface
+    public interface Channel {
+        void send(String text);
+    }
+
+    public enum Severity { INFO, WARNING, CRITICAL }
+
+    private static final Logger log = LoggerFactory.getLogger(AlertDispatcher.class);
+
+    private final Map<String, Channel> channels = new ConcurrentHashMap<>();
+

Review this PR

Java practice