DEV Community

Cover image for Cnator: channel-based subscriptions in Go
Altiano Gerung
Altiano Gerung

Posted on

Cnator: channel-based subscriptions in Go

Channels is one of those things that makes Go awesome.

I use channels quite a lot in my next open projects. One of those scenario is to model an event-driven approach.

While developing these projects, I noticed that the setup is more or less the same, so I decided to make a Go module for that.

It's called Cnator (pronounce "c-nator")

go get
Enter fullscreen mode Exit fullscreen mode

It has 3 methods :

  • New()
  • Subscribe(channel, subscriber)
  • Serve()

that can be use it like this

    cnator := cnator.New()
    channels := createChannels()

    // provide channel reference to each publisher
    producerA := NewProducerA(channels.chanA)
    producerB := NewProducerB(channels.chanB)
    producerC := NewProducerC(channels.chanC)

    // using cnator to subscribe to those channel events
    subscriberA := subscriberA{}
    subscriberB := subscriberB{}
    subscriberC := subscriberC{}
    cnator.Subscribe(channels.chanA, subscriberA.receiveChanA)
    cnator.Subscribe(channels.chanB, subscriberB.receiveChanB)
    cnator.Subscribe(channels.chanC, subscriberC.receiveChanC)

     // start watching for events
Enter fullscreen mode Exit fullscreen mode

createChannel() just initialized the channels, but you should provide your own model

func createChannels() channels {
    return channels{
        chanA: make(chan struct{}),
        chanB: make(chan string),
        chanC: make(chan Person),
Enter fullscreen mode Exit fullscreen mode

the subscribers are just callback functions with a matching data type with the channels

func (s *subscriberA) receiveChanA() {
    fmt.Println("Subscriber A receiving")

func (s *subscriberB) receiveChanB(data string) {
    fmt.Println("Subscriber B receiving", data)

func (s *subscriberC) receiveChanC(data Person) {
    fmt.Println("Subscriber C receiving", data)
Enter fullscreen mode Exit fullscreen mode

except for chanA that receive empty struct{},
you can ignore the parameter like receiveChanA() does.

The job of cnator.Serve() is to spawn a go routine for each subscription made by cnator.Subscribe(..).

It provides some runtime validation like

  • whether the subscriber function doesn't satisfy the channel data type or
  • whether the channel has not been initialized (i.e. forget to make(chan ..)) etc

Full code & examples can be found at this repository : Cnator

Also posted on :

Top comments (0)