Cnator: channel-based subscriptions in Go

By Altiano Gerung, on May 26, 2020

Cnator

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 gitlab.com/altiano/cnator

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
    cnator.Serve()

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),
	}
}

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)
}

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 dev.to
© 2020 Altiano R. Gerung-Terms-Privacy