9.6 KiB
9.6 KiB
Notifier Microservice Architecture
Overview
The Notifier microservice is designed to provide a flexible, scalable notification delivery system that supports multiple notification channels (SMTP, Slack, Ntfy, Stdout) with both REST and gRPC APIs.
Architecture Principles
- Clean Architecture: Domain-driven design with clear separation of concerns
- Interface-based Design: All core components use interfaces for maximum flexibility
- Pluggable Notifiers: Easy to add new notification providers
- Queue Abstraction: Supports both local and distributed queues (Kafka)
- Configuration-driven: Viper-based configuration with environment variable support
- Cloud-native: Containerized with Kubernetes support
Core Components
1. Domain Layer (internal/domain/)
The domain layer defines the core business logic and interfaces:
notification.go
- Notification: Core notification entity with metadata, status tracking, and retry logic
- Priority: Enumeration for notification urgency (Low, Normal, High, Critical)
- NotificationType: Supported notification channels (Email, Slack, Ntfy, Stdout)
- NotificationStatus: Lifecycle states (Pending, Queued, Processing, Sent, Failed, Retrying)
- NotificationResult: Outcome of notification delivery attempts
- NotificationFilter: Query interface for retrieving notifications
notifier.go
- Notifier: Core interface that all notification implementations must satisfy
- NotifierFactory: Factory pattern for creating notifier instances
- NotificationService: High-level service interface for notification operations
- NotificationStats: Statistics and metrics about notification processing
queue.go
- Queue: Interface for notification queue implementations
- QueueMessage: Wrapper around notifications with queue-specific metadata
- QueueConfig: Configuration for queue implementations
- LocalQueueConfig: In-memory queue configuration
- KafkaQueueConfig: Distributed Kafka queue configuration
2. Queue Implementation (internal/queue/)
local.go
- In-memory queue implementation using Go channels
- Optional disk persistence for durability
- Thread-safe with mutex protection
- Supports enqueue, dequeue, ack, and nack operations
- Configurable buffer size and retry behavior
3. Notifier Implementations (internal/notifier/)
notifier.go
- Factory: Manages and creates notifier instances
- BaseNotifier: Common functionality shared by all notifiers
- Validation and context checking utilities
stdout.go
- Simple stdout notifier for debugging and development
- Prints notifications to console with formatted output
smtp.go
- Email notifications via SMTP
- Supports TLS/SSL
- Configurable SMTP server, port, and authentication
- RFC-compliant email message formatting
ntfy.go
- Integration with ntfy.sh push notification service
- Supports custom ntfy servers
- Priority mapping and metadata support
- Rich notification features (tags, click actions, attachments)
slack.go
- Slack webhook integration
- Channel-specific webhook support
- Rich message formatting with blocks
- Priority indicators and custom branding
4. Configuration (internal/config/)
config.go
- Viper-based configuration management
- Support for YAML config files and environment variables
- Default value handling
- Comprehensive validation
- Hierarchical configuration structure:
- Server settings (ports, host, mode)
- Queue configuration
- Notifier credentials
- Logging settings
- Metrics and observability
- Health check configuration
5. API Layer
gRPC API (api/grpc/)
notifier.proto
- Protocol buffer definitions for the gRPC service
- Operations:
SendNotification: Send single notificationSendBatchNotifications: Send multiple notificationsGetNotification: Retrieve notification by IDListNotifications: Query notifications with filtersCancelNotification: Cancel pending notificationRetryNotification: Retry failed notificationGetStats: Retrieve service statisticsHealthCheck: Service health verification
REST API (api/rest/)
handlers.go
- HTTP handlers for REST endpoints
- Request validation and error handling
- JSON serialization/deserialization
router.go
- Gorilla mux router configuration
- Middleware for logging and CORS
- Route definitions matching gRPC operations
types.go
- REST API request/response types
- Domain model conversions
- Validation logic
6. Entry Points (cmd/)
grpcserver/main.go
- Standalone gRPC server
- Service initialization and dependency injection
- Graceful shutdown handling
restserver/main.go
- Standalone REST server
- HTTP server configuration
- Graceful shutdown handling
Data Flow
Sending a Notification
Client Request (REST/gRPC)
↓
API Handler
↓
NotificationService.Send()
↓
Queue.Enqueue()
↓
[Notification queued]
↓
Worker dequeues (Queue.Dequeue())
↓
NotifierFactory.Create()
↓
Notifier.Send()
↓
Provider API (SMTP/Slack/Ntfy/Stdout)
↓
Queue.Ack() or Queue.Nack()
↓
Update notification status
↓
[Notification sent or failed]
Queue Strategies
Local Queue
- In-memory implementation using Go channels
- Fast and simple for single-instance deployments
- Optional disk persistence for durability
- Suitable for development and small-scale production
Kafka Queue (Future)
- Distributed queue for multi-instance deployments
- Guarantees delivery across service restarts
- Horizontal scalability
- Exactly-once semantics with idempotence
- Suitable for high-throughput production environments
Notification Lifecycle
- Pending: Notification created but not yet queued
- Queued: Added to queue, waiting for processing
- Processing: Worker has dequeued and is sending
- Sent: Successfully delivered to provider
- Failed: Delivery failed after max retries
- Retrying: Temporarily failed, will retry
Retry Strategy
- Configurable max retries (default: 3)
- Pluggable backoff strategies:
- Exponential: 2^n delay between retries
- Linear: Fixed increment between retries
- Fixed: Constant delay between retries
Configuration Management
Hierarchy (highest to lowest priority)
- Environment variables (prefixed with
NOTIFIER_) - Configuration file (notifier.config)
- Default values
Example Environment Variables
NOTIFIER_SERVER_GRPC_PORT=50051
NOTIFIER_SERVER_REST_PORT=8080
NOTIFIER_QUEUE_TYPE=local
NOTIFIER_NOTIFIERS_SMTP_HOST=smtp.gmail.com
NOTIFIER_NOTIFIERS_SLACK_WEBHOOK_URL=https://hooks.slack.com/...
Deployment Strategies
Docker Compose
- Single-host deployment
- All components in one compose file
- Optional Kafka, Prometheus, and Grafana services
- Volume mounts for configuration and persistence
Kubernetes
- Multi-instance deployment with HPA
- Separate services for REST and gRPC
- ConfigMaps for configuration
- Secrets for credentials
- Ingress for external access
- Health checks and readiness probes
- Resource limits and requests
Scaling Considerations
Horizontal Scaling
- Multiple instances can run concurrently
- Use Kafka queue for distributed message processing
- Stateless design allows easy scaling
Vertical Scaling
- Increase worker count per instance
- Tune queue buffer sizes
- Adjust resource limits
Observability
Metrics (Prometheus)
- Total notifications sent/failed
- Notifications by type and status
- Average latency
- Queue size
- Worker utilization
Health Checks
- Dedicated health endpoint
- Component-level health reporting
- Kubernetes liveness/readiness probes
Logging
- Structured JSON logging
- Configurable log levels
- Request/response logging
- Error tracking
Security
Authentication
- API keys for REST endpoints (to be implemented)
- mTLS for gRPC (to be implemented)
- Kubernetes RBAC for service account
Authorization
- Per-notifier credential management
- Secrets stored in Kubernetes Secrets or external secret manager
- No credentials in configuration files
Network Security
- Non-root container user
- Read-only root filesystem where possible
- Minimal container image (Alpine-based)
- Security context restrictions
Extension Points
Adding a New Notifier
- Create new file in
internal/notifier/ - Implement the
domain.Notifierinterface:Send(ctx, notification) -> resultType() -> NotificationTypeValidate(notification) -> errorClose() -> error
- Add configuration struct to
internal/config/ - Register in factory during initialization
- Update protobuf and REST API types
- Add configuration example to
notifier.config
Adding a New Queue Implementation
- Create new file in
internal/queue/ - Implement the
domain.Queueinterface - Add configuration to
domain.QueueConfig - Update queue factory/initialization logic
- Add configuration example
Next Steps for Implementation
- Generate Protocol Buffers: Run
make proto-gento generate gRPC code - Implement Service Layer: Create the main service that ties everything together
- Implement Server Main Functions: Wire up dependencies in
cmd/ - Add Tests: Unit tests for notifiers, integration tests for queue
- Add Kafka Queue: Implement distributed queue using Kafka
- Add Metrics: Prometheus instrumentation
- Add Authentication: API key or OAuth support
- Add Rate Limiting: Prevent abuse and manage provider quotas
- Add Notification Templates: Support for templated messages
- Add Webhooks: Allow callbacks on notification status changes