A robust, multi-channel notification service built with Spring Boot that enables sending notifications across multiple platforms including Email, SMS, and WhatsApp.
The Notification Service is a microservice designed to handle notification delivery across multiple channels. It provides a unified API for sending notifications through different providers while managing notification logs, templates, and configurations with intelligent provider selection and fallback mechanisms.
Project Group ID: com.axd
Version: 0.0.1-SNAPSHOT
Java Version: 21
Spring Boot Version: 3.5.7
API Base Path: /notification-service
-
Multi-Channel Support:
- Email (AWS SES, Postmark)
- SMS (Twilio, AWS SNS)
- WhatsApp (Twilio)
-
Key Capabilities:
- Template-based notification system with dynamic content rendering
- Request deduplication using
requestIdto prevent duplicate sends - Notification history and detailed logging
- Provider selection with configurable rankings and fallback mechanisms
- Configuration management with Redis caching for high performance
- User notification tracking and status retrieval
- Support for both template-based and direct content notifications
-
Technology Stack:
- Spring Boot 3.5.7
- Spring Data JPA with Hibernate
- MySQL Database
- AWS SDK (SES, SNS)
- Twilio SDK
- Lombok for Code Generation
- SpringDoc OpenAPI/Swagger Documentation
Before you begin, ensure you have the following installed:
- Java 21
- Maven 3.6+
- MySQL 8.0+
git clone https://github.com/yourusername/notification-service.git
cd notification-servicemvn clean installmvn spring-boot:runThe application will start on http://localhost:8080/notification-service
All endpoints are prefixed with /notification-service/notifications
Endpoint: POST /notifications/send
Request Body:
{
"requestId": "unique-request-id-12345",
"userId": 1,
"type": "TEMPLATE_BASED",
"channel": "EMAIL",
"data": {
"templateId": "welcome_email",
"userName": "John Doe",
"activationLink": "https://example.com/activate",
"recipientEmail": "john@example.com"
}
}Alternative - Direct Content:
{
"requestId": "unique-request-id-67890",
"userId": 2,
"type": "DIRECT_CONTENT",
"channel": "SMS",
"data": {
"phoneNumber": "+1234567890",
"message": "Your verification code is: 123456"
}
}Response:
{
"success": true,
"data": {
"notificationId": "550e8400-e29b-41d4-a716-446655440000",
"status": "SUCCESS"
}
}Endpoint: GET /notifications/user/{userId}
Response:
{
"success": true,
"data": {
"userId": 1,
"notifications": [
{
"id": 1,
"notificationId": "550e8400-e29b-41d4-a716-446655440000",
"channel": "EMAIL",
"provider": "AWS_SES",
"state": "SUCCESS",
"createdAt": "2026-03-03T10:30:00"
}
]
}
}Endpoint: GET /notifications/status/{notificationId}
Response:
{
"success": true,
"data": {
"status": "SUCCESS"
}
}- AWS SES - High deliverability, enterprise-grade
- Postmark - Developer-friendly email service
- Twilio SMS - Global SMS delivery
- AWS SNS - AWS-integrated SMS service
- Twilio WhatsApp - Rich media messaging, interactive templates
notification-service/
├── src/
│ ├── main/
│ │ ├── java/com/axd/notificationService/
│ │ │ ├── channel/
│ │ │ │ ├── implementations/
│ │ │ │ │ ├── EmailChannelService.java
│ │ │ │ │ ├── SmsChannelService.java
│ │ │ │ │ └── WhatsAppChannelService.java
│ │ │ │ └── NotificationChannelService.java
│ │ │ ├── config/
│ │ │ │ ├── AwsSesConfig.java
│ │ │ │ ├── AwsSnsConfig.java
│ │ │ │ ├── TwilioConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── KafkaConfig.java
│ │ │ │ └── WebClientConfig.java
│ │ │ ├── controller/
│ │ │ │ └── NotificationController.java
│ │ │ ├── enums/
│ │ │ │ ├── NotificationChannel.java # EMAIL, SMS, WHATSAPP
│ │ │ │ ├── NotificationProvider.java # AWS_SES, TWILIO_SMS, etc.
│ │ │ │ ├── NotificationType.java # TEMPLATE_BASED, DIRECT_CONTENT
│ │ │ │ ├── NotificationRequestStatus.java
│ │ │ │ └── LogContextKey.java
│ │ │ ├── exceptions/
│ │ │ │ ├── AbstractException.java
│ │ │ │ ├── ValidationException.java
│ │ │ │ ├── ResourceNotFoundException.java
│ │ │ │ └── handler/GlobalExceptionsHandler.java
│ │ │ ├── factory/
│ │ │ │ ├── ChannelFactory.java
│ │ │ │ └── ProviderFactory.java
│ │ │ ├── model/
│ │ │ │ ├── entity/
│ │ │ │ │ ├── BaseEntity.java
│ │ │ │ │ ├── User.java
│ │ │ │ │ ├── NotificationLog.java
│ │ │ │ │ ├── NotificationTemplate.java
│ │ │ │ │ └── ConfigEntity.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── request/NotificationRequest.java
│ │ │ │ │ ├── response/
│ │ │ │ │ │ ├── GenericResponse.java
│ │ │ │ │ │ ├── NotificationResponse.java
│ │ │ │ │ │ ├── NotificationStatus.java
│ │ │ │ │ │ └── UserNotification.java
│ │ │ │ │ ├── EmailDto.java
│ │ │ │ │ ├── SmsDto.java
│ │ │ │ │ └── WhatsappDto.java
│ │ │ │ └── ProviderConfig.java
│ │ │ ├── provider/
│ │ │ │ ├── NotificationProviderService.java
│ │ │ │ ├── emailProvider/AwsEmailProviderService.java
│ │ │ │ ├── smsProvider/
│ │ │ │ │ ├── AwsSmsProviderService.java
│ │ │ │ │ └── TwilioSmsProviderService.java
│ │ │ │ └── whatsAppProvider/TwilioWhatsAppProviderService.java
│ │ │ ├── repository/
│ │ │ │ ├── UserRepository.java
│ │ │ │ ├── NotificationLogRepository.java
│ │ │ │ ├── NotificationTemplateRepository.java
│ │ │ │ └── ConfigRepository.java
│ │ │ ├── service/
│ │ │ │ ├── NotificationService.java
│ │ │ │ ├── NotificationLogService.java
│ │ │ │ ├── ConfigCacheService.java
│ │ │ │ └── ProviderSelectionService.java
│ │ │ ├── util/
│ │ │ │ ├── TemplateRenderer.java
│ │ │ │ └── CommonUtils.java
│ │ │ ├── constants/
│ │ │ │ ├── AppConstants.java
│ │ │ │ └── ControllerConstants.java
│ │ │ └── NotificationServiceApplication.java
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ └── java/com/axd/notificationService/
├── pom.xml
├── README.md
└── HELP.md
- Core service orchestrating notification delivery
- Handles request deduplication using
requestId - Routes requests to appropriate channels via
ChannelFactory - Manages notification status and user notifications retrieval
- Abstract contract for all channel implementations
- Implemented by
EmailChannelService,SmsChannelService,WhatsAppChannelService - Each implementation selects appropriate providers using
ProviderSelectionService
- ChannelFactory: Maps
NotificationChannelenum to channel service implementations - ProviderFactory: Dynamically selects providers based on configuration and availability
- Selects providers for a channel based on cached configuration
- Filters enabled providers and sorts by rank
- Implements fallback mechanism - if primary provider fails, tries next in rank
- Manages configuration caching with Redis
- Eliminates repeated database queries for provider configurations
- Supports cache invalidation and refresh
- Logs all notification attempts and their outcomes
- Tracks status, provider used, and delivery results
- Enables notification history and debugging
@Entity
public class User {
private Long id;
private String name;
}@Entity
public class NotificationLog extends BaseEntity {
private Long id;
private String requestId; // Used for deduplication
private String notificationId;
private Long userId;
private String eventId;
private String type; // TEMPLATE_BASED, DIRECT_CONTENT
private String channel; // EMAIL, SMS, WHATSAPP
private String provider; // AWS_SES, TWILIO_SMS, etc.
private String providerNotificationId;
private String state; // SUCCESS, FAILURE, PENDING
}@Entity
public class NotificationTemplate extends BaseEntity {
private Long id;
private String type; // Template type (e.g., ORDER_PLACED)
private String channel; // EMAIL, SMS
private String subject; // For email templates
private String bodyTemplate; // Template with placeholders
}@Entity
public class ConfigEntity extends BaseEntity {
private Long id;
@Column(name = "`group`")
private String group; // Configuration group (EMAIL, SMS, etc.)
@Column(name = "`key`")
private String key; // Configuration key
private String value; // Configuration value
}@MappedSuperclass
public class BaseEntity {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}Configuration is stored in the database and cached in Redis for performance:
// Example: Providers for EMAIL channel stored as JSON
{
"group": "CHANNEL_PROVIDERS",
"key": "EMAIL",
"value": {
"providers": [
{
"name": "AWS_SES",
"enabled": true,
"rank": 1
},
{
"name": "POSTMARK",
"enabled": true,
"rank": 2
}
]
}
}1. Receive Notification Request (POST /notifications/send)
↓
2. Validate Request & Check Required Fields
↓
3. Check Deduplication (Query by requestId)
└─→ If exists, return existing notification ID
↓
4. Determine Channel from Request
↓
5. Get Channel Implementation via ChannelFactory
↓
6. Channel Service:
├─ Get Providers via ProviderSelectionService
├─ Try providers in rank order
├─ If provider succeeds → return result
└─ If provider fails → try next provider (fallback)
↓
7. Log Notification (if not failure)
↓
8. Return Response with notificationId & status
- Uses pre-configured templates with placeholders
- Requires
templateIdin request data - Template variables substituted at runtime
- Example: Welcome email with dynamic user name
- Sends content directly without templates
- Message/body provided in request
- No template lookup required
- Example: OTP or verification code
The application uses SLF4J with Logback for comprehensive logging:
- Request-level logging: Track requests through the system
- Provider-level logging: Monitor each provider's performance
- Error logging: Capture and log exceptions with stack traces
- Contextual information: Log through
LogContextKeyenumeration
Run the test suite:
mvn testTest class: NotificationServiceApplicationTests.java
Create a Dockerfile in the project root:
FROM openjdk:21-slim
WORKDIR /app
COPY target/notification-service-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]Build and run:
docker build -t notification-service:latest .
docker run -p 8080:8080 \
-e SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/notification_db \
-e SPRING_DATASOURCE_USERNAME=root \
-e SPRING_DATASOURCE_PASSWORD=root \
notification-service:latestversion: '3.8'
services:
notification-service:
build: .
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/notification_db
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: root
SPRING_DATA_REDIS_HOST: redis
SPRING_DATA_REDIS_PORT: 6379
AWS_SES_ACCESS_KEY: ${AWS_ACCESS_KEY}
AWS_SES_SECRET_KEY: ${AWS_SECRET_KEY}
TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID}
TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN}
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: notification_db
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:7.0-alpine
ports:
- "6379:6379"
volumes:
mysql_data:Interactive API documentation is available via Swagger UI:
http://localhost:8080/notification-service/swagger-ui.html
OpenAPI specification:
http://localhost:8080/notification-service/v3/api-docs