Commit 2ea19d0

mo khan <mo@mokhan.ca>
2025-07-24 18:30:01
feat: extract a strongly typed aggregator
1 parent 58f695c
pkg/event/aggregator.go
@@ -17,15 +17,6 @@ func (a *Aggregator) Subscribe(event Event, f Subscription) {
 	a.subscriptions[event] = append(a.subscriptions[event], f)
 }
 
-func (a *Aggregator) SubscribeTo[T any](event Event, f func(T)) {
-	wrapper := func(message any) {
-		if typedMessage, ok := message.(T); ok {
-			f(typedMessage)
-		}
-	}
-	a.Subscribe(event, wrapper)
-}
-
 func (a *Aggregator) Publish(event Event, message any) {
 	for _, subscription := range a.subscriptions[event] {
 		subscription(message)
pkg/event/aggregator_test.go
@@ -6,7 +6,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestEventAggregator(t *testing.T) {
+func TestAggregator(t *testing.T) {
 	t.Run("Publish", func(t *testing.T) {
 		t.Run("without any subscribers", func(t *testing.T) {
 			aggregator := New()
@@ -48,19 +48,5 @@ func TestEventAggregator(t *testing.T) {
 			assert.True(t, called[0])
 			assert.True(t, called[1])
 		})
-
-		t.Run("with a strongly typed payload", func(t *testing.T) {
-			aggregator := New()
-
-			type announcement struct {
-				message string
-			}
-
-			aggregator.SubscribeTo("announcement", func(payload announcement) {
-				assert.Equal(t, "Hello", payload.message)
-			})
-
-			aggregator.Publish("announcement", announcement{message: "Hello"})
-		})
 	})
 }
pkg/event/typed_aggregator.go
@@ -0,0 +1,24 @@
+package event
+
+type TypedAggregator[T any] struct {
+	aggregator *Aggregator
+}
+
+func NewTypedAggregator[T any]() *TypedAggregator[T] {
+	return &TypedAggregator[T]{
+		aggregator: New(),
+	}
+}
+
+func (a *TypedAggregator[T]) SubscribeTo(event Event, f func(T)) {
+	a.aggregator.Subscribe(event, func(message any) {
+		if item, ok := message.(T); ok {
+			f(item)
+		}
+	})
+
+}
+
+func (a *TypedAggregator[T]) Publish(event Event, message T) {
+	a.aggregator.Publish(event, message)
+}
pkg/event/typed_aggregator_test.go
@@ -0,0 +1,60 @@
+package event
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestTypedAggregator(t *testing.T) {
+	type announcement struct {
+		message string
+	}
+
+	t.Run("Publish", func(t *testing.T) {
+		t.Run("without any subscribers", func(t *testing.T) {
+			aggregator := NewTypedAggregator[announcement]()
+
+			aggregator.Publish("announcement", announcement{
+				message: "Business, Business, Business... Numbers!",
+			})
+		})
+
+		t.Run("with a single subscription", func(t *testing.T) {
+			called := false
+			aggregator := NewTypedAggregator[announcement]()
+
+			aggregator.SubscribeTo("announcement", func(payload announcement) {
+				called = true
+				assert.Equal(t, "Hello", payload.message)
+			})
+
+			aggregator.Publish("announcement", announcement{message: "Hello"})
+
+			assert.True(t, called)
+		})
+
+		t.Run("with multiple subscribers", func(t *testing.T) {
+			aggregator := NewTypedAggregator[announcement]()
+			called := map[int]bool{}
+
+			aggregator.SubscribeTo("announcement", func(payload announcement) {
+				called[0] = true
+				assert.Equal(t, "Greetings", payload.message)
+			})
+
+			aggregator.SubscribeTo("announcement", func(payload announcement) {
+				called[1] = true
+				assert.Equal(t, "Greetings", payload.message)
+			})
+
+			aggregator.Publish("announcement", announcement{
+				message: "Greetings",
+			})
+
+			assert.Equal(t, 2, len(called))
+			assert.True(t, called[0])
+			assert.True(t, called[1])
+		})
+	})
+}