mob alternatives and similar packages
Based on the "Messaging" category.
Alternatively, view mob alternatives based on common mentions on social networks and blogs.
-
sarama
DISCONTINUED. Sarama is a Go library for Apache Kafka. [Moved to: https://github.com/IBM/sarama] -
Centrifugo
Scalable real-time messaging server in a language-agnostic way. Self-hosted alternative to Pubnub, Pusher, Ably. Set up once and forever. -
Benthos
DISCONTINUED. Fancy stream processing made operationally mundane [Moved to: https://github.com/redpanda-data/connect] -
APNs2
⚡ HTTP/2 Apple Push Notification Service (APNs) push provider for Go — Send push notifications to iOS, tvOS, Safari and OSX apps, using the APNs HTTP/2 protocol. -
Uniqush-Push
Uniqush is a free and open source software system which provides a unified push service for server side notification to apps on mobile devices. -
amqp
An AMQP 0-9-1 Go client maintained by the RabbitMQ team. Originally by @streadway: `streadway/amqp` -
Chanify
Chanify is a safe and simple notification tools. This repository is command line tools for Chanify. -
PingMe
PingMe is a CLI which provides the ability to send messages or alerts to multiple messaging platforms & email. -
emitter
Emits events in Go way, with wildcard, predicates, cancellation possibilities and many other good wins -
Bus
🔊Minimalist message bus implementation for internal communication with zero-allocation magic on Emit -
go-mq
Declare AMQP entities like queues, producers, and consumers in a declarative way. Can be used to work with RabbitMQ. -
Ratus
Ratus is a RESTful asynchronous task queue server. It translated concepts of distributed task queues into a set of resources that conform to REST principles and provides a consistent HTTP API for various backends. -
RapidMQ
RapidMQ is a pure, extremely productive, lightweight and reliable library for managing of the local messages queue
InfluxDB - Purpose built for real-time analytics at any scale.
Do you think we are missing an alternative of mob or a related project?
README
mob
mob
is a generic-based, simple mediator / observer library.
It supports in-process requests / events processing.
Motivation
I was a bit tired of managing dependencies between handlers. Reusing them became the existential issue. That's how mob
has been created. It solves complex dependency management by introducing a single communication point. The mediator part encapsulates request-response communication while the observer one acts as a facade focused on observer relationships. mob
is conceptually similiar to Event aggregator described by Martin Fowler.
mob
supports two types of handlers - request handlers and event handlers.
Request handlers
A request handler responses to a particular request.
Request handlers can be registered through the RegisterRequestHandler
method.
type DummyHandler struct{}
func(DummyHandler) Name() string {
return "DummyHandler"
}
func (DummyHandler) Handle(ctx context.Context, req DummyRequest) (DummyResponse, error) {
// Logic.
}
...
func main() {
handler := DummyHandler{}
if err := mob.RegisterRequestHandler[DummyRequest, DummyResponse](handler); err != nil {
log.Fatalf("register handler %s: %v", handler.Name(), err)
}
}
A handler to register must satisfy the RequestHandler
interface. Both request and response can have arbitrary data types.
Only one handler for a particular request-response pair can be registered. To avoid handlers conflicts use type alias declarations.
To send a request and get a response simply call the Send
method.
// Somewhere in your code.
response, err := mob.Send[DummyRequest, DummyResponse](ctx, req)
If a handler does not exist for a given request - response pair - ErrHandlerNotFound
is returned.
Event handlers
An event handler executes some logic in response to a dispatched event.
Event handlers can be registered through the RegisterEventHandler
method.
type DummyHandler struct{}
func(DummyHandler) Name() string {
return "DummyHandler"
}
func (DummyHandler) Handle(ctx context.Context, req DummyRequest) error {
// Logic.
}
...
func main() {
handler := DummyHandler{}
if err := mob.RegisterEventHandler[DummyRequest](handler); err != nil {
log.Fatalf("register handler %s: %v", handler.Name(), err)
}
}
A handler to register must satisfy the EventHandler
interface. A request can have an arbitrary data type.
Event handlers are almost identical to the request ones. There are a few subtle differences though. An event handler does not return a response, only an error in case of failure. Unlike request ones, multiple handlers for a given request type can be registered. Be careful, mob
doesn't check if a concrete handler is registered multiple times. Type alias declarations solves handler conflicts.
To notify all registered handlers about a certain event, call the Notify
method.
// Somewhere in your code.
err := mob.Notify(ctx, event)
mob
executes all registered handlers concurrently. If at least one of them fails, an aggregate error containing all errors is returned.
Concurrency
mob
is a concurrent-safe library for multiple requests and events processing. But you shouldn't mix handlers' registration with requests or events processing. mob
assumes that clients register their handlers during the initialization process and after first request or event is processed - no handler is registered.
Use cases
There are many use cases for mob
. Everytime when there is a burden of dependency management, mob
can become a useful friend.
There are two cases where I find mob
extremely useful.
The first one is to slim the application layer API handlers. mob
centralizes control so there is no need to use DI. It makes the components more portable.
The following example shows one of the most popular kind of the application layers handlers - HTTP handlers.
Classic way
func GetUserHandler(u UserGetter) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
var dureq DummyUserRequest
_ = json.NewDecoder(req.Body).Decode(&dureq)
res, _ := u.Get(req.Context(), dureq)
rw.Header().Set("content-type", "application/json")
rw.WriteHeader(http.StatusOK)
_ = json.NewEncoder(rw).Encode(res)
}
}
mob
way
func GetUser(rw http.ResponseWriter, req *http.Request) {
var dureq DummyUserRequest
_ = json.NewDecoder(req.Body).Decode(&dureq)
res, _ := mob.Send[DummyUserRequest, DummyUserResponse](req.Context(), dureq)
rw.Header().Set("content-type", "application/json")
rw.WriteHeader(http.StatusOK)
_ = json.NewEncoder(rw).Encode(res)
}
mob
is a convenient tool for applying CQS and CQRS.
mob
also makes it easier to take advantage of any kind of in-process, event-based communication. A domain event processing is a great example.
Classic way
func (s *UserService) UpdateEmail(ctx context.Context, id string, email string) error {
u, _ := s.Repository.GetUser(ctx, id)
u.Email = email
_ = s.Repository.UpdateUser(ctx, u)
_ = s.ContactBookService.RefreshContactBook(ctx)
_ = s.NewsletterService.RefreshNewsletterContactInformation(ctx)
// Do more side-effect actions in response to the email changed event.
return nil
}
mob
way
func (s *UserService) UpdateEmail(ctx context.Context, id string, email string) error {
u, _ := s.Repository.GetUser(ctx, id)
u.Email = email
_ = s.Repository.UpdateUser(ctx, u)
_ = mob.Notify(ctx, EmailChanged{UserID: id, Email: email})
return nil
}
Conclusion
Although mob
can be exteremely useful. It has some drawbacks. It makes an explicit communication implicit - in many cases a direct communication is much better than an indirect one. Also, where performance is a critical factor, you'd rather go with the explicit communication - it's always faster to call a handler directly.