- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
441 lines
11 KiB
Markdown
441 lines
11 KiB
Markdown
# Notification System
|
|
|
|
Multi-channel notification system with support for email, database, push, SMS, and webhooks.
|
|
|
|
## Features
|
|
|
|
- **Multi-Channel Delivery**: Email, Database (in-app), Push, SMS, Webhook
|
|
- **Queue Integration**: Async delivery via framework Queue system
|
|
- **Event System**: NotificationSent and NotificationFailed events
|
|
- **Priority Levels**: LOW, NORMAL, HIGH, URGENT
|
|
- **Read Tracking**: Mark notifications as read, count unread
|
|
- **Action Buttons**: Optional action URL and label
|
|
- **Type Safety**: Fully typed with Value Objects and Enums
|
|
- **Framework Integration**: Uses existing Mail, Queue, EventBus, Database modules
|
|
|
|
## Basic Usage
|
|
|
|
### Creating and Sending a Notification
|
|
|
|
```php
|
|
use App\Framework\Notification\Notification;
|
|
use App\Framework\Notification\NotificationDispatcher;
|
|
use App\Framework\Notification\ValueObjects\NotificationChannel;
|
|
use App\Framework\Notification\ValueObjects\NotificationPriority;
|
|
use App\Framework\Notification\ValueObjects\NotificationType;
|
|
|
|
// Create notification
|
|
$notification = Notification::create(
|
|
recipientId: 'user-123',
|
|
type: NotificationType::system(),
|
|
title: 'System Update',
|
|
body: 'Your system has been updated to version 2.0',
|
|
NotificationChannel::DATABASE,
|
|
NotificationChannel::EMAIL
|
|
);
|
|
|
|
// Add optional features
|
|
$notification = $notification
|
|
->withPriority(NotificationPriority::HIGH)
|
|
->withAction('/changelog', 'View Changelog')
|
|
->withData([
|
|
'version' => '2.0.0',
|
|
'features' => ['performance', 'security']
|
|
]);
|
|
|
|
// Send asynchronously (via Queue)
|
|
$dispatcher->sendLater($notification);
|
|
|
|
// Or send immediately
|
|
$result = $dispatcher->sendNow($notification);
|
|
```
|
|
|
|
### Common Notification Types
|
|
|
|
```php
|
|
// System notifications
|
|
NotificationType::system()
|
|
|
|
// Security alerts
|
|
NotificationType::security()
|
|
|
|
// Marketing messages
|
|
NotificationType::marketing()
|
|
|
|
// Social interactions
|
|
NotificationType::social()
|
|
|
|
// Transactional emails
|
|
NotificationType::transactional()
|
|
|
|
// Custom types
|
|
NotificationType::fromString('order-status')
|
|
```
|
|
|
|
### Retrieving Notifications
|
|
|
|
```php
|
|
use App\Framework\Notification\Storage\NotificationRepository;
|
|
|
|
// Get user's notifications
|
|
$notifications = $repository->findByUser('user-123', limit: 20);
|
|
|
|
// Get unread notifications
|
|
$unread = $repository->findUnreadByUser('user-123');
|
|
|
|
// Count unread
|
|
$count = $repository->countUnreadByUser('user-123');
|
|
|
|
// Mark as read
|
|
$repository->markAsRead($notificationId);
|
|
|
|
// Mark all as read
|
|
$repository->markAllAsReadForUser('user-123');
|
|
```
|
|
|
|
## Channels
|
|
|
|
### Database Channel (In-App Notifications)
|
|
|
|
Stores notifications in database for user inbox/notification center.
|
|
|
|
```php
|
|
NotificationChannel::DATABASE
|
|
```
|
|
|
|
**Features**:
|
|
- Persistent storage
|
|
- Read/unread tracking
|
|
- Pagination support
|
|
- Automatic cleanup of old notifications
|
|
|
|
### Email Channel
|
|
|
|
Sends notifications via email using framework's Mail module.
|
|
|
|
```php
|
|
NotificationChannel::EMAIL
|
|
```
|
|
|
|
**Features**:
|
|
- HTML and plain text emails
|
|
- Priority mapping
|
|
- Action buttons in email
|
|
- Automatic HTML formatting
|
|
|
|
**Requirements**:
|
|
- UserEmailResolver implementation to map user IDs to email addresses
|
|
|
|
```php
|
|
class DatabaseUserEmailResolver implements UserEmailResolver
|
|
{
|
|
public function resolveEmail(string $userId): ?Email
|
|
{
|
|
// Lookup user email from database
|
|
return $this->userRepository->findEmailById($userId);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Push Channel (Placeholder)
|
|
|
|
For web push and mobile push notifications.
|
|
|
|
```php
|
|
NotificationChannel::PUSH
|
|
```
|
|
|
|
**Status**: Interface defined, implementation needed.
|
|
|
|
### SMS Channel (Placeholder)
|
|
|
|
For SMS notifications via external provider.
|
|
|
|
```php
|
|
NotificationChannel::SMS
|
|
```
|
|
|
|
**Status**: Interface defined, implementation needed.
|
|
|
|
### Webhook Channel (Placeholder)
|
|
|
|
For sending notifications to external systems via HTTP webhooks.
|
|
|
|
```php
|
|
NotificationChannel::WEBHOOK
|
|
```
|
|
|
|
**Status**: Interface defined, implementation needed.
|
|
|
|
## Queue Integration
|
|
|
|
The notification system integrates seamlessly with the framework's Queue system for asynchronous delivery.
|
|
|
|
### Async Delivery
|
|
|
|
```php
|
|
// Queue for background processing
|
|
$dispatcher->sendLater($notification);
|
|
|
|
// Priority is mapped from notification priority:
|
|
// URGENT → HIGH
|
|
// HIGH → MEDIUM
|
|
// NORMAL → LOW
|
|
// LOW → LOW
|
|
```
|
|
|
|
### Immediate Delivery
|
|
|
|
```php
|
|
// Send immediately (blocks until complete)
|
|
$result = $dispatcher->sendNow($notification);
|
|
|
|
if ($result->isSuccess()) {
|
|
echo "Sent via: " . count($result->getSuccessful()) . " channels\n";
|
|
} else {
|
|
echo "Errors: " . implode(', ', $result->getErrors()) . "\n";
|
|
}
|
|
```
|
|
|
|
## Event System
|
|
|
|
The notification system dispatches events via the framework's EventBus.
|
|
|
|
### NotificationSent Event
|
|
|
|
Dispatched when a notification is successfully sent via at least one channel.
|
|
|
|
```php
|
|
use App\Framework\Notification\Events\NotificationSent;
|
|
use App\Framework\EventBus\Attributes\EventHandler;
|
|
|
|
#[EventHandler]
|
|
final class NotificationLogger
|
|
{
|
|
public function handleNotificationSent(NotificationSent $event): void
|
|
{
|
|
$this->logger->info('Notification sent', [
|
|
'notification_id' => $event->notification->id->toString(),
|
|
'recipient' => $event->notification->recipientId,
|
|
'channels' => count($event->result->getSuccessful())
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
### NotificationFailed Event
|
|
|
|
Dispatched when a notification fails on all channels.
|
|
|
|
```php
|
|
use App\Framework\Notification\Events\NotificationFailed;
|
|
use App\Framework\EventBus\Attributes\EventHandler;
|
|
|
|
#[EventHandler]
|
|
final class NotificationFailureHandler
|
|
{
|
|
public function handleNotificationFailure(NotificationFailed $event): void
|
|
{
|
|
$this->alerting->sendAlert(
|
|
'Notification delivery failed',
|
|
$event->result->getErrors()
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Database Schema
|
|
|
|
The notifications table is created via migration:
|
|
|
|
```php
|
|
src/Framework/Notification/Migrations/CreateNotificationsTable.php
|
|
```
|
|
|
|
**Table Structure**:
|
|
- `id` (ULID) - Primary key
|
|
- `recipient_id` - User/entity receiving notification
|
|
- `type` - Notification category
|
|
- `title` - Notification title
|
|
- `body` - Notification message
|
|
- `data` - JSON structured data
|
|
- `channels` - JSON array of delivery channels
|
|
- `priority` - Delivery priority
|
|
- `status` - Current status (pending, sent, delivered, failed, read, archived)
|
|
- `created_at` - Creation timestamp
|
|
- `sent_at` - Delivery timestamp
|
|
- `read_at` - Read timestamp
|
|
- `action_url` - Optional action URL
|
|
- `action_label` - Optional action button label
|
|
|
|
**Indexes**:
|
|
- `recipient_id` + `status` + `created_at` (composite)
|
|
- `recipient_id` + `type` (composite)
|
|
- `read_at` (for unread queries)
|
|
|
|
## Configuration
|
|
|
|
### Setting Up Channels
|
|
|
|
```php
|
|
// In your Initializer
|
|
use App\Framework\Notification\Channels\DatabaseChannel;
|
|
use App\Framework\Notification\Channels\EmailChannel;
|
|
use App\Framework\Notification\NotificationDispatcher;
|
|
|
|
$container->singleton(NotificationDispatcher::class, function($c) {
|
|
return new NotificationDispatcher(
|
|
channels: [
|
|
$c->get(DatabaseChannel::class),
|
|
$c->get(EmailChannel::class),
|
|
// Add more channels as needed
|
|
],
|
|
queue: $c->get(Queue::class),
|
|
eventBus: $c->get(EventBus::class)
|
|
);
|
|
});
|
|
```
|
|
|
|
### Email Channel Setup
|
|
|
|
```php
|
|
$container->singleton(EmailChannel::class, function($c) {
|
|
return new EmailChannel(
|
|
mailer: $c->get(MailerInterface::class),
|
|
fromAddress: new Email('notifications@example.com'),
|
|
userEmailResolver: $c->get(UserEmailResolver::class)
|
|
);
|
|
});
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Use Async Delivery**: Always prefer `sendLater()` for better performance
|
|
2. **Set Appropriate Priority**: Reserve URGENT for critical notifications
|
|
3. **Include Actions**: Add action URLs when user interaction is expected
|
|
4. **Structured Data**: Use `withData()` for additional context
|
|
5. **Type Classification**: Use proper NotificationType for filtering
|
|
6. **Cleanup Old Notifications**: Periodically delete old read/archived notifications
|
|
|
|
## Examples
|
|
|
|
### Welcome Notification
|
|
|
|
```php
|
|
$notification = Notification::create(
|
|
recipientId: $user->id,
|
|
type: NotificationType::system(),
|
|
title: 'Welcome to Our Platform!',
|
|
body: 'Thank you for signing up. Get started by completing your profile.',
|
|
NotificationChannel::DATABASE,
|
|
NotificationChannel::EMAIL
|
|
)->withAction('/profile/complete', 'Complete Profile');
|
|
|
|
$dispatcher->sendLater($notification);
|
|
```
|
|
|
|
### Security Alert
|
|
|
|
```php
|
|
$notification = Notification::create(
|
|
recipientId: $user->id,
|
|
type: NotificationType::security(),
|
|
title: 'New Login Detected',
|
|
body: "We detected a login from {$location} at {$time}",
|
|
NotificationChannel::DATABASE,
|
|
NotificationChannel::EMAIL
|
|
)
|
|
->withPriority(NotificationPriority::HIGH)
|
|
->withData([
|
|
'ip_address' => $ipAddress,
|
|
'location' => $location,
|
|
'device' => $device
|
|
]);
|
|
|
|
$dispatcher->sendNow($notification); // Immediate for security
|
|
```
|
|
|
|
### Order Confirmation
|
|
|
|
```php
|
|
$notification = Notification::create(
|
|
recipientId: $order->userId,
|
|
type: NotificationType::transactional(),
|
|
title: 'Order Confirmed',
|
|
body: "Your order #{$order->number} has been confirmed",
|
|
NotificationChannel::EMAIL
|
|
)
|
|
->withData([
|
|
'order_id' => $order->id,
|
|
'total' => $order->total->toDecimal(),
|
|
'items' => count($order->items)
|
|
])
|
|
->withAction("/orders/{$order->id}", 'View Order');
|
|
|
|
$dispatcher->sendLater($notification);
|
|
```
|
|
|
|
## Testing
|
|
|
|
Run tests with:
|
|
|
|
```bash
|
|
./vendor/bin/pest tests/Feature/NotificationSystemTest.php
|
|
```
|
|
|
|
## Extending the System
|
|
|
|
### Adding a New Channel
|
|
|
|
1. Implement `NotificationChannelInterface`
|
|
2. Add channel to `NotificationChannel` enum
|
|
3. Register channel in dispatcher initialization
|
|
4. Implement `send()` method with delivery logic
|
|
|
|
```php
|
|
final readonly class SmsChannel implements NotificationChannelInterface
|
|
{
|
|
public function __construct(
|
|
private SmsProvider $provider
|
|
) {}
|
|
|
|
public function send(Notification $notification): ChannelResult
|
|
{
|
|
// Implementation
|
|
}
|
|
|
|
public function supports(Notification $notification): bool
|
|
{
|
|
return $notification->supportsChannel(NotificationChannel::SMS);
|
|
}
|
|
|
|
public function getChannel(): NotificationChannel
|
|
{
|
|
return NotificationChannel::SMS;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Architecture
|
|
|
|
The notification system follows framework principles:
|
|
|
|
- ✅ **Readonly Value Objects**: Notification, NotificationId, etc.
|
|
- ✅ **Final Classes**: All implementation classes are final
|
|
- ✅ **Composition over Inheritance**: Channel interface composition
|
|
- ✅ **Event-Driven**: Integration with EventBus
|
|
- ✅ **Queue Integration**: Async delivery via Queue system
|
|
- ✅ **Module Reuse**: Leverages Mail, Queue, EventBus, Database modules
|
|
|
|
## Future Enhancements
|
|
|
|
- [ ] User preference management for notification types
|
|
- [ ] Notification templates with variables
|
|
- [ ] Push notification implementation (Web Push API)
|
|
- [ ] SMS channel implementation
|
|
- [ ] Webhook channel implementation
|
|
- [ ] Notification batching/digest mode
|
|
- [ ] Quiet hours / Do Not Disturb
|
|
- [ ] A/B testing for notification content
|
|
- [ ] Analytics tracking
|