Tier: Adapter · Status: Full (port + dispatcher + memory channel) · Java original:
firefly-notifications· .NET project:FireflyFramework.Notifications
notifications is the channel-agnostic notification port:
Notification— the message envelope (channel, recipient, body, optional template, optional variables).Channel— the transport interface (Kind,Send,Name).Dispatcher— fans messages out to channels keyed onKind.MemoryChannel— a default in-process channel that records every message sent (useful for tests).
Concrete provider adapters (notificationssendgrid,
notificationsresend, notificationstwilio,
notificationsfirebase) live in dedicated modules and currently
ship as port-asserting stubs — see docs/AUDIT.md
§ Roadmap.
type Kind string // KindEmail | KindSMS | KindPush
type Notification struct {
ID string
Channel Kind
To string
Subject string
Body string
Template string
Variables map[string]any
CreatedAt time.Time
}
type Channel interface {
Kind() Kind
Send(ctx, Notification) error
Name() string
}
type Dispatcher struct{ ... }
func NewDispatcher() *Dispatcher
func (*Dispatcher) Register(Channel)
func (*Dispatcher) Dispatch(ctx, Notification) error // ErrNoChannel if Kind not registered
type MemoryChannel struct{ K Kind; Messages []Notification }
func NewMemoryChannel(Kind) *MemoryChannel
var ErrNoChannel = errors.New("firefly/notifications: no channel registered")import (
"context"
"github.com/fireflyframework/fireflyframework-go/notifications"
)
d := notifications.NewDispatcher()
d.Register(notifications.NewMemoryChannel(notifications.KindEmail))
d.Register(notifications.NewMemoryChannel(notifications.KindSMS))
_ = d.Dispatch(ctx, notifications.Notification{
Channel: notifications.KindEmail,
To: "alice@example.com",
Subject: "Welcome",
Body: "Welcome to Firefly!",
})For production, register notificationssendgrid.New(...) instead of
MemoryChannel(KindEmail) — same interface, real delivery.
cd notifications
go test ./...Covers dispatch routing by channel Kind and the ErrNoChannel
sentinel for unrouted messages.