Cloud-agnostic asynchronous messaging with abstract interfaces and implementations for NATS, RabbitMQ, and Google Pub/Sub. Trigger: When implementing event-driven communication, publishing domain events, or creating event subscribers.
| Pattern | Rule |
|---|---|
| Interface in domain | EventPublisher is a domain port |
| Implementation swappable | NATS, RabbitMQ, or Pub/Sub behind same interface |
| Events are immutable facts | Past tense: BookingCreated, PaymentCompleted |
| Events carry data | Include enough data so consumers don't need to call back |
| Idempotent consumers | Subscribers MUST handle duplicate messages |
| Application layer never imports messaging SDK |
Reference: assets/event.go
Reference: assets/port.go
Reference: assets/nats_publisher.go
Reference: assets/nats_subscriber.go
{service}.{domain}.{event}
Examples:
booking.booking.created
booking.booking.cancelled
payment.transaction.completed
caregiver.profile.verified
notification.email.requested
Reference: assets/publish_usage.go
Reference: assets/memory_publisher.go
# NATS
brew install nats-server
nats-server # Start locally
# Dependencies
go get github.com/nats-io/nats.go
# RabbitMQ alternative
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management
go get github.com/rabbitmq/amqp091-go
| ❌ Don't | ✅ Do |
|---|---|
| Import NATS/RabbitMQ in domain | Define EventPublisher interface in domain |
Events named as commands (CreateBooking) | Events are past tense facts (BookingCreated) |
| Consumer calls back to producer service | Include enough data in the event payload |
| Assume exactly-once delivery | Design idempotent consumers |
| Synchronous event publishing blocking the request | Use goroutines or accept best-effort for non-critical events |