Какие существуют паттерны для реализации ‘триггеров’ или ‘хуков’ в Go?

Ответ

В языке Go нет встроенной концепции "триггеров", как, например, в SQL. Однако эта функциональность реализуется с помощью различных программных паттернов, которые позволяют выполнить код в ответ на определённое событие. Такие паттерны часто называют "хуками" (hooks) или "коллбэками" (callbacks).

Вот основные способы их реализации:

1. Хуки в ORM (на примере GORM)

Многие ORM-библиотеки предоставляют хуки, которые автоматически вызываются до или после операций с базой данных. Это самый прямой аналог триггеров.

  • BeforeSave, AfterSave
  • BeforeCreate, AfterCreate
  • BeforeUpdate, AfterUpdate
  • BeforeDelete, AfterDelete

Пример (GORM):

import "gorm.io/gorm"

type User struct {
    gorm.Model
    Name  string
    Email string
}

// AfterCreate - это хук, который GORM вызовет после создания новой записи User.
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    fmt.Printf("Отправляем приветственное письмо на %sn", u.Email)
    // Здесь может быть логика отправки email
    return
}

2. Использование каналов для событий

Это идиоматичный для Go способ организации асинхронных "триггеров". Одна часть системы отправляет событие в канал, а другая (подписчик) слушает этот канал и реагирует на событие.

Пример:

// Канал для событий о создании нового пользователя
var userCreatedEvents = make(chan string, 10)

// Функция-обработчик ("триггер"), которая слушает события
func emailNotificationListener() {
    for email := range userCreatedEvents {
        fmt.Printf("Listener: отправка письма на %sn", email)
    }
}

func main() {
    // Запускаем слушателя в отдельной горутине
    go emailNotificationListener()

    // Имитируем создание пользователей, что "триггерит" отправку событий
    userCreatedEvents <- "user1@example.com"
    userCreatedEvents <- "user2@example.com"

    time.Sleep(1 * time.Second) // Даем время на обработку
    close(userCreatedEvents)
}

3. Функции обратного вызова (Callbacks)

Функция может принимать другую функцию в качестве аргумента и вызывать её в нужный момент. Это классический паттерн коллбэка.

Пример:

// Функция, которая выполняет некую работу и вызывает коллбэк по завершении
func processData(data string, onComplete func(result string)) {
    fmt.Println("Начинаю обработку...")
    // ... какая-то работа ...
    result := "Обработано: " + data
    onComplete(result) // Вызов "триггера"-коллбэка
}

func main() {
    // Передаем лямбда-функцию в качестве коллбэка
    processData("мой файл", func(res string) {
        fmt.Println("Задача завершена! Результат:", res)
    })
}