Skip to content

chahario/Notification-Service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Notification Service

A lightweight Spring Boot notification service that publishes and consumes notification events via Kafka, supports multiple delivery channels (email, SMS, push), and uses an outbox pattern for reliable delivery.

Contents

Overview

This project implements a notification microservice built with Spring Boot and Apache Kafka. It receives requests to create notifications, persists them, emits domain events to Kafka, consumes events, and processes delivery through pluggable channels.

Architecture

Architecture diagram (high-level):

Architecture diagram

Diagram notes:

Key Components

Data Flow

  1. Client hits POST /notify handled by NotificationController.
  2. Controller calls NotificationService which validates and persists a Notification entity.
  3. A corresponding OutboxEvent is created and saved to the outbox table.
  4. OutboxPublisher publishes the event to Kafka topics.
  5. NotificationConsumer consumes events and hands them to NotificationProcessor.
  6. NotificationProcessor routes to a channel via ChannelFactory (email/sms/push) and attempts delivery.
  7. If delivery fails, retry logic in RetryWorker/RetryBackoffCalculator handles backoff and dead-lettering (DlqProducer).

Codebase Evaluation

  • Idempotency is implemented at request-level. NotificationRequest includes idempotencyKey, NotificationService checks notificationRepository.findByIdempotencyKey(...), and the Notification entity has a unique database index on idempotencyKey.
  • Ordering is supported through Kafka partitioning. KafkaTopicConfig creates notification-events and notification-dlq with 3 partitions, while NotificationProducer uses userId_type as the Kafka key.
  • Reliability is strong via the outbox pattern. OutboxEvent records are persisted first and published later by OutboxPublisher, reducing the risk of lost events during transient broker failures.
  • Failure handling exists for both retries and dead-lettering. RetryWorker / RetryBackoffCalculator manage retry attempts, and NotificationProcessor uses DlqProducer to send failed events to the DLQ.
  • Important implementation note: There is a topic name mismatch risk in the current codebase: NotificationProducer sends to notifications-events while NotificationConsumer listens on notification-events. This should be aligned before production use.
  • Response semantics: The API returns success after persistence and outbox creation; delivery is eventually consistent, not synchronous.
  • Code organization: Clear separation exists between API, domain service, persistence, messaging infra, and worker/processor layers, which makes the service easier to maintain and extend.

Decisions & Tradeoffs

  • Outbox Pattern: chosen to guarantee atomicity between DB writes and Kafka publishes in environments without distributed transactions. Tradeoff: additional storage and publishing complexity; requires a background publisher (OutboxPublisher).
  • Kafka for async delivery: provides scalability and decoupling. Tradeoff: operational complexity (brokers, partitioning, monitoring) versus simpler alternatives like in-process queues.
  • Pluggable Channels: ChannelFactory enables switching/adding channels with minimal code changes. Tradeoff: slightly more indirection and abstraction.
  • Retry Strategy: exponential backoff via RetryBackoffCalculator and dedicated RetryWorker gives robust delivery attempts. Tradeoff: increased implementation complexity and potential duplicate-delivery handling requirements.
  • DLQ (Dead Letter Queue): failed events are routed to a DLQ topic using DlqProducer. This simplifies failure handling but requires human/operator processes to inspect and repair DLQ messages.
  • Synchronous API response: The controller responds quickly after persisting and scheduling an outbox publish, improving client latency at the cost of eventual consistency for delivery.

How to run

  • Start Kafka and Zookeeper (local or docker)
# With Docker Compose (if you have Kafka compose services available)
docker-compose up -d

# Build and run the app
./mvnw clean package
./mvnw spring-boot:run

Where to look in the codebase

Testing

  • Unit tests are under src/test/java (run with ./mvnw test).
  • Integration tests should run against a Kafka test fixture or a testcontainer-based Kafka.

Future improvements

  • Extend request-level idempotency to stronger exactly-once semantics and replay-safe outbox processing.
  • Add metrics and distributed tracing (Prometheus + OpenTelemetry).
  • Provide Kubernetes manifests and Helm chart for production deployment.
  • Add automated DLQ replay tooling and a small web UI for observability.

About

Spring Boot Kafka notification service with outbox pattern, partitioned ordered delivery, retry/DLQ handling, and idempotent notification creation.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages