Снова про
[T Constraint[T]]. На этом можно построить событийную шину, где обработчик получает конкретный тип события, а не interface{}.Событийные системы в Go обычно строят одним из двух способов. Либо типизированные каналы, и тогда нужен отдельный канал на каждый тип события, что быстро разрастается. Либо диспатч через
interface{} с приведением типа в каждом обработчике. Второй путь гибче, но каждое приведение это место, где код упадёт в рантайме, если тип не совпал. Компилятор при этом молчит.Как это работает
Событие несёт свой тип полезной нагрузки через самоссылку, а обработчик и шина параметризуются этим же типом:
type Event[T any] interface {
EventType() string
Payload() T
}
type Handler[T Event[T]] func(ctx context.Context, event T) error
type TypedBus[T Event[T]] struct {
mu sync.RWMutex
handlers []Handler[T]
}
func (b *TypedBus[T]) Subscribe(h Handler[T]) {
b.mu.Lock()
b.handlers = append(b.handlers, h)
b.mu.Unlock()
}
func (b *TypedBus[T]) Publish(ctx context.Context, event T) error {
b.mu.RLock()
handlers := b.handlers
b.mu.RUnlock()
for _, h := range handlers {
if err := h(ctx, event); err != nil {
return err
}
}
return nil
}На стороне вызова обработчик получает конкретный тип, а не пустой интерфейс:
bus := &TypedBus[*UserEvent]{}
bus.Subscribe(func(ctx context.Context, e *UserEvent) error {
log.Println(e.UserID) // реальные поля, без приведения
return nil
})Добавить новый тип события значит создать новый
TypedBus[NewEventType]. Если типы обработчика и шины не сойдутся, компилятор сообщит об этом до выката, текстом вроде *UserEvent does not implement Event[*UserEvent]. Сообщение разбирается не с первого взгляда, но оно появляется на сборке, а не в проде.Самоссылка даёт третий путь между отдельным каналом на каждый тип и универсальным диспатчем через
interface{}. Обработчик работает с настоящими полями события, а несоответствие типов ловится компилятором. Для событийных систем это убирает целую категорию рантайм-багов с приведением типов и делает добавление нового события предсказуемым.📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction