CorebanqCorebanq Developer Docs
Notifications

Integration

Overview

This guide provides comprehensive instructions for integrating the CoreBanq notification service into your modules. The notification system supports multilingual content, multiple delivery channels, and user preference management.

Quick Start

1. Service Initialization

Add the notification service to your module handler:

package yourmodule

    "corebanq/modules/notifications"
    // other imports...
)

type Handler struct {
    storage             storage.CoreBanqStorage
    notificationService *notifications.NotificationService
    // other fields...
}

func NewHandler(ctx context.Context, storage storage.CoreBanqStorage, qs queue.Store, db *gorm.DB) *Handler {
    return &Handler{
        storage:             storage,
        notificationService: notifications.NewNotificationService(ctx, qs, db),
    }
}

2. Basic Notification

notification := &notifications.Notification{
    Type:       notifications.NotificationTypeTask,
    Event:      "account_created",
    Title:      "Account Created",
    Content:    "Your new account has been successfully created",
    CustomerID: customerID,
    UserIDs:    []uuid.UUID{userID},
    Metadata: map[string]any{
        "account_id": accountID.String(),
    },
}

err := notificationService.SendNotification(ctx, notification)

Integration Patterns

Module Initialization Pattern

type Module struct {
    Handler             *Handler
    NotificationService *notifications.NotificationService
}

func InitModule(ctx context.Context, db *gorm.DB, qs queue.Store, storage storage.CoreBanqStorage) *Module {
    notificationService := notifications.NewNotificationService(ctx, qs, db)
    
    handler := &Handler{
        storage:             storage,
        notificationService: notificationService,
    }
    
    return &Module{
        Handler:             handler,
        NotificationService: notificationService,
    }
}

Practical Examples

Account Creation Notification

func (h *Handler) sendAccountCreatedNotification(ctx context.Context, userID, customerID, accountID uuid.UUID) error {
    // Get user's preferred language
    language := h.notificationService.GetUserPreferredLanguage(ctx, userID, customerID)
    
    // Get localized messages
    title := h.notificationService.GetLocalizedMessage(ctx, userID, customerID, "accounts_m.created_title")
    content := h.notificationService.GetLocalizedMessage(ctx, userID, customerID, "accounts_m.created_content")
    
    // Generate contextual link
    linkParams := map[string]string{
        "account_id": accountID.String(),
    }
    linkURL, linkText := h.notificationService.GetLinkInfo("account_created", language, linkParams)
    
    notification := &notifications.Notification{
        Type:       notifications.NotificationTypeTask,
        Event:      "account_created",
        Title:      title,
        Content:    content,
        LinkURL:    linkURL,
        LinkText:   linkText,
        CustomerID: customerID,
        UserIDs:    []uuid.UUID{userID},
        Metadata: map[string]any{
            "account_id": accountID.String(),
            "event_type": "account_created",
        },
    }
    
    // Send notification (non-blocking)
    if err := h.notificationService.SendNotification(ctx, notification); err != nil {
        logger.Errorf("Failed to send account created notification: %v", err)
        // Don't fail the main operation
    }
    
    return nil
}

Chat Message Notification

func (h *Handler) sendChatNotification(ctx context.Context, params ChatNotificationParams) error {
    // Get room participants (excluding sender)
    participants, err := h.storage.GetRoomParticipants(params.RoomID)
    if err != nil {
        return fmt.Errorf("failed to get room participants: %w", err)
    }
    
    room, err := h.storage.GetChatRoomByID(params.RoomID)
    if err != nil {
        return fmt.Errorf("failed to get chat room: %w", err)
    }
    
    var recipientIDs []uuid.UUID
    for _, participant := range participants {
        if participant.UserID != params.SenderID {
            recipientIDs = append(recipientIDs, participant.UserID)
        }
    }
    
    if len(recipientIDs) == 0 {
        return nil
    }
    
    // Get localized content
    language := h.notificationService.GetUserPreferredLanguage(ctx, params.SenderID, room.CustomerID)
    title := h.notificationService.GetLocalizedMessage(ctx, params.SenderID, room.CustomerID, "chat_m.new_message_title")
    content := h.notificationService.GetLocalizedMessage(ctx, params.SenderID, room.CustomerID, "chat_m.new_message_content")
    
    // Generate link
    linkParams := map[string]string{
        "room_id":    params.RoomID.String(),
        "message_id": params.MessageID.String(),
    }
    linkURL, linkText := h.notificationService.GetLinkInfo("new_message", language, linkParams)
    
    notification := &notifications.Notification{
        Type:       notifications.NotificationTypeChat,
        Event:      "message_received",
        Title:      title,
        Content:    content,
        LinkURL:    linkURL,
        LinkText:   linkText,
        CustomerID: room.CustomerID,
        UserIDs:    recipientIDs,
        Metadata: map[string]any{
            "room_id":    params.RoomID.String(),
            "message_id": params.MessageID.String(),
            "sender_id":  params.SenderID.String(),
        },
    }
    
    return h.notificationService.SendNotification(ctx, notification)
}

type ChatNotificationParams struct {
    RoomID    uuid.UUID
    SenderID  uuid.UUID
    MessageID uuid.UUID
    Content   string
}

Transaction Notification

func (h *Handler) notifyTransactionCompleted(ctx context.Context, transaction *models.Transaction) error {
    userIDs := []uuid.UUID{transaction.SenderID}
    if transaction.ReceiverID != uuid.Nil {
        userIDs = append(userIDs, transaction.ReceiverID)
    }
    
    for _, userID := range userIDs {
        language := h.notificationService.GetUserPreferredLanguage(ctx, userID, transaction.CustomerID)
        
        title := h.notificationService.GetLocalizedMessage(ctx, userID, transaction.CustomerID, "transactions_m.completed_title")
        content := h.notificationService.GetLocalizedMessage(ctx, userID, transaction.CustomerID, "transactions_m.completed_content")
        
        linkParams := map[string]string{
            "transaction_id": transaction.ID.String(),
        }
        linkURL, linkText := h.notificationService.GetLinkInfo("transaction_completed", language, linkParams)
        
        notification := &notifications.Notification{
            Type:       notifications.NotificationTypeTask,
            Event:      "transaction_completed",
            Title:      title,
            Content:    content,
            LinkURL:    linkURL,
            LinkText:   linkText,
            CustomerID: transaction.CustomerID,
            UserIDs:    []uuid.UUID{userID},
            Metadata: map[string]any{
                "transaction_id": transaction.ID.String(),
                "amount":         transaction.Amount,
                "currency":       transaction.Currency,
                "type":           transaction.Type,
            },
        }
        
        if err := h.notificationService.SendNotification(ctx, notification); err != nil {
            logger.Errorf("Failed to send transaction notification to user %s: %v", userID, err)
        }
    }
    
    return nil
}

Bulk System Notification

func (h *Handler) notifySystemMaintenance(ctx context.Context, customerID uuid.UUID, maintenanceTime time.Time) error {
    // Get all active users for the customer
    users, err := h.storage.GetActiveUsersByCustomer(customerID)
    if err != nil {
        return fmt.Errorf("failed to get active users: %w", err)
    }
    
    var userIDs []uuid.UUID
    for _, user := range users {
        userIDs = append(userIDs, user.ID)
    }
    
    if len(userIDs) == 0 {
        return nil
    }
    
    language := h.notificationService.GetUserPreferredLanguage(ctx, userIDs[0], customerID)
    title := h.notificationService.GetLocalizedMessage(ctx, userIDs[0], customerID, "system_m.maintenance_title")
    content := h.notificationService.GetLocalizedMessage(ctx, userIDs[0], customerID, "system_m.maintenance_content")
    
    notification := &notifications.Notification{
        Type:       notifications.NotificationTypeTask,
        Event:      "system_maintenance",
        Title:      title,
        Content:    content,
        CustomerID: customerID,
        UserIDs:    userIDs,
        Metadata: map[string]any{
            "maintenance_time": maintenanceTime.Format(time.RFC3339),
            "event_type":       "system_maintenance",
        },
    }
    
    return h.notificationService.SendNotification(ctx, notification)
}

Conditional Notification with User Preferences

func (h *Handler) notifyBalanceThreshold(ctx context.Context, account *models.Account, threshold float64) error {
    // Check if user has enabled balance notifications
    preferences, err := h.notificationService.GetNotificationPreferences(account.UserID, account.CustomerID)
    if err != nil {
        logger.Errorf("Failed to get notification preferences: %v", err)
        return nil
    }
    
    // Check if balance notifications are enabled
    balanceNotificationsEnabled := false
    for _, group := range preferences.Groups {
        if group.Type == "balance_alerts" && group.Enabled {
            for _, event := range group.Events {
                if event.Type == "low_balance" && event.Enabled {
                    balanceNotificationsEnabled = true
                    break
                }
            }
        }
    }
    
    if !balanceNotificationsEnabled {
        return nil // User has disabled these notifications
    }
    
    language := h.notificationService.GetUserPreferredLanguage(ctx, account.UserID, account.CustomerID)
    title := h.notificationService.GetLocalizedMessage(ctx, account.UserID, account.CustomerID, "balance_m.low_balance_title")
    content := h.notificationService.GetLocalizedMessage(ctx, account.UserID, account.CustomerID, "balance_m.low_balance_content")
    
    linkParams := map[string]string{
        "account_id": account.ID.String(),
    }
    linkURL, linkText := h.notificationService.GetLinkInfo("low_balance", language, linkParams)
    
    notification := &notifications.Notification{
        Type:       notifications.NotificationTypeTask,
        Event:      "low_balance",
        Title:      title,
        Content:    content,
        LinkURL:    linkURL,
        LinkText:   linkText,
        CustomerID: account.CustomerID,
        UserIDs:    []uuid.UUID{account.UserID},
        Metadata: map[string]any{
            "account_id":       account.ID.String(),
            "current_balance":  account.Balance,
            "threshold":        threshold,
            "currency":         account.Currency,
        },
    }
    
    return h.notificationService.SendNotification(ctx, notification)
}

Configuration

Message Configuration

Create config/samples/yourmodule_m.yaml:

messages:
  account_created_title:
    en: "Account Created"
    de: "Konto erstellt"
    fr: "Compte créé"
    it: "Account creato"
  account_created_content:
    en: "Your new account has been successfully created"
    de: "Ihr neues Konto wurde erfolgreich erstellt"
    fr: "Votre nouveau compte a été créé avec succès"
    it: "Il tuo nuovo account è stato creato con successo"
  transaction_completed_title:
    en: "Transaction Completed"
    de: "Transaktion abgeschlossen"
    fr: "Transaction terminée"
    it: "Transazione completata"
  transaction_completed_content:
    en: "Your transaction has been processed successfully"
    de: "Ihre Transaktion wurde erfolgreich verarbeitet"
    fr: "Votre transaction a été traitée avec succès"
    it: "La tua transazione è stata elaborata con successo"

Add to config/samples/notifications.yaml:

links:
  account_created:
    link: "/accounts/{account_id}"
    title:
      en: "View Account"
      de: "Konto anzeigen"
      fr: "Voir le compte"
      it: "Visualizza account"
  transaction_completed:
    link: "/transactions/{transaction_id}"
    title:
      en: "View Transaction"
      de: "Transaktion anzeigen"
      fr: "Voir la transaction"
      it: "Visualizza transazione"

events:
  account_created:
    link: "/accounts/{account_id}"
    title:
      en: "View Account"
      de: "Konto anzeigen"
      fr: "Voir le compte"
      it: "Visualizza account"

Notification Groups

Add to config/samples/notifications.yaml:

groups:
  - title:
      en: "Account Management"
      de: "Kontoverwaltung"
      fr: "Gestion de compte"
      it: "Gestione account"
    type: "accounts"
    enabled: true
    channels:
      - title:
          en: "Email"
          de: "E-Mail"
          fr: "E-mail"
          it: "Email"
        type: "email"
      - title:
          en: "System"
          de: "System"
          fr: "Système"
          it: "Sistema"
        type: "system"
    events:
      - title:
          en: "Account Created"
          de: "Konto erstellt"
          fr: "Compte créé"
          it: "Account creato"
        type: "account_created"
        description:
          en: "Notify when a new account is created"
          de: "Benachrichtigung, wenn ein neues Konto erstellt wird"
          fr: "Notifier lorsqu'un nouveau compte est créé"
          it: "Notifica quando viene creato un nuovo account"
        enabled: true

Best Practices

Error Handling

Always handle notification errors gracefully:

if err := h.notificationService.SendNotification(ctx, notification); err != nil {
    logger.Errorf("Failed to send notification: %v", err)
    // Log the error but don't fail the main operation
}

Non-blocking Integration

Send notifications asynchronously to avoid blocking main operations:

// Process main operation first
if err := h.processMainOperation(); err != nil {
    return err
}

// Send notification in background
go func() {
    if err := h.sendNotification(context.Background(), notification); err != nil {
        logger.Errorf("Failed to send notification: %v", err)
    }
}()

Retry with Backoff

For critical notifications, implement retry logic:

func (h *Handler) sendNotificationWithRetry(ctx context.Context, notification *notifications.Notification) error {
    maxRetries := 3
    backoff := time.Second
    
    for i := 0; i < maxRetries; i++ {
        if err := h.notificationService.SendNotification(ctx, notification); err != nil {
            if i == maxRetries-1 {
                return fmt.Errorf("failed after %d retries: %w", maxRetries, err)
            }
            logger.Warnf("Notification failed (attempt %d/%d): %v", i+1, maxRetries, err)
            time.Sleep(backoff)
            backoff *= 2
            continue
        }
        return nil
    }
    return nil
}

Rich Metadata

Include relevant metadata for debugging and filtering:

notification := &notifications.Notification{
    // ... other fields
    Metadata: map[string]any{
        "entity_id":   entityID.String(),
        "entity_type": "order",
        "action":      "created",
        "timestamp":   time.Now().Unix(),
        "module":      "orders",
        "version":     "1.0",
    },
}

Language Handling

Always get user's preferred language with fallback:

language := h.notificationService.GetUserPreferredLanguage(ctx, userID, customerID)
title := h.notificationService.GetLocalizedMessageWithFallback(
    ctx, userID, customerID,
    "yourmodule_m.notification_title",
    "Default Title", // fallback
)

API Reference

Core Methods

  • SendNotification(ctx, notification) - Send a notification
  • GetUserPreferredLanguage(ctx, userID, customerID) - Get user's language preference
  • GetLocalizedMessage(ctx, userID, customerID, messageKey) - Get localized message
  • GetLocalizedMessageWithFallback(ctx, userID, customerID, messageKey, fallback) - Get localized message with fallback
  • GetLinkInfo(event, language, params) - Generate contextual links
  • GetNotificationPreferences(userID, customerID) - Get user notification preferences
  • UpdateNotificationPreferences(userID, customerID, groups) - Update user preferences

Notification Types

const (
    NotificationTypeChat NotificationType = "chat"
    NotificationTypeTask NotificationType = "task"
)

Delivery Channels

const (
    ChannelEmail            DeliveryChannel = "email"
    ChannelSMS              DeliveryChannel = "sms"
    ChannelSystem           DeliveryChannel = "system"
    ChannelPushNotification DeliveryChannel = "push_notification"
)

Testing

Unit Test Example

func TestSendAccountNotification(t *testing.T) {
    ctx := context.Background()
    mockStorage := &MockStorage{}
    mockNotificationService := &MockNotificationService{}
    
    handler := &Handler{
        storage:             mockStorage,
        notificationService: mockNotificationService,
    }
    
    userID := uuid.New()
    customerID := uuid.New()
    accountID := uuid.New()
    
    err := handler.sendAccountCreatedNotification(ctx, userID, customerID, accountID)
    
    assert.NoError(t, err)
    assert.Equal(t, 1, mockNotificationService.SendNotificationCallCount)
    
    sentNotification := mockNotificationService.LastSentNotification
    assert.Equal(t, notifications.NotificationTypeTask, sentNotification.Type)
    assert.Equal(t, "account_created", sentNotification.Event)
    assert.Equal(t, customerID, sentNotification.CustomerID)
    assert.Contains(t, sentNotification.UserIDs, userID)
    assert.Equal(t, accountID.String(), sentNotification.Metadata["account_id"])
}

Architecture

The notification system follows a queue-based architecture:

  1. Notification Creation - Notifications are created and queued
  2. Queue Processing - Background workers process notifications
  3. Delivery - Notifications are delivered via configured channels
  4. State Tracking - Delivery and read states are tracked
  5. Real-time Updates - SSE provides instant notifications to connected clients

This ensures reliable delivery, scalability, and real-time user experience.

On this page