An interface to add domain events and messages to your Go projects!
View Examples
·
Report Bug
·
Request Feature
As a project matures, requirements and the technical environment evolve, it is painful to realise that deeply embedded dependencies like loggers or message systems have to be replaced. This is especially true for Go, that is not offering standard abstractions for these use cases. Thus, it is a helpful practise to hide external libraries behind an application specific interface.
This is such an (opinionated) interface intended to be copied into your project.
The goal is a message interface, that can be implemented by multiple libraries
as time progresses.
This repository explores what a good interface looks like for Go and
provides an in memory reference implementation.
- Simplicity. The most important part is the simple interface. It provides just enough to send and receive messages in an application.
- Change resilient. Don't just import this project. Copy the interface into your app and use any of the available implementations, so your use cases and domain don't depend on any library in the future. Not even on this one.
- Developer convenience.
With an emphasis on development speed and readable code
this interface improves upon the pattern often seen with other libraries:
marshalling and unmarshalling the message to and from
[]byte
. This clutters your code with serialisation logic and can be hidden behind an interface.
type PubSub interface {
Publish(ctx context.Context, eom EventOrMessage) error
Subscribe(eom EventOrMessage, h HandlerFunc) (*Subscriber, error)
Shutdown(ctx context.Context)
}
$ go get -u github.com/HTechHQ/message
Publish-Subscribe with messages
func main() {
p := message.NewPubsubMem()
ctx := context.Background()
sub, _ := p.Subscribe(helloMessage{}, func(ctx context.Context, msg helloMessage) {
fmt.Println(msg.Message)
})
p.Publish(ctx, helloMessage{"hello world!"})
sub.Unsubscribe()
p.Publish(ctx, helloMessage{"hello world!"})
p.Shutdown(ctx)
// output: hello world!
}
// helloMessage is a message passed from a publisher to potentially many subscribers.
type helloMessage struct {
Message string
}
Publish-Subscribe with domain events
func main() {
var publisher message.PubSub = message.NewPubsubMem()
publisher.Subscribe(newUserRegistered{}, func(ctx context.Context, e newUserRegistered) {
// log the registration of the new user
fmt.Println(e.Username, e.Email, e.Settings, e.CreatedAt.Format("2006.01.02"))
})
publisher.Subscribe(newUserRegistered{}, func(ctx context.Context, e newUserRegistered) {
fmt.Printf("Preparing welcome email for new user: %s to: %s\n", e.Username, e.Email)
})
publisher.Publish(context.Background(), newUserRegistered{
Username: "Max",
Email: "[email protected]",
CreatedAt: time.Now(),
Settings: []string{"setting"},
})
publisher.Shutdown(context.Background())
// output:
// Max [setting] 2021.04.25
// Preparing welcome email for new user: Max to: [email protected]
}
// newUserRegistered is the actual event that's fired after a new user got registered.
type newUserRegistered struct {
Username string
Email string
CreatedAt time.Time
Settings []string
}
See all examples
Depending on your needs a persistent implementation, relying on something like NATS or Redis, might be good to support your desired semantics like at-least-once-delivery. PR welcome.
Also working on metrics and a queue expansion.
See the open issues for a list of proposed features (and known issues).