Mastering Domain Events in Go: Definition, Publishing, and Consumption
This article explains what domain events are, how to define them with proper naming and attributes, explores multiple techniques for publishing events in Go—including parameter injection, static-like helpers, return‑based and repository‑based approaches—and describes strategies for consuming events safely and atomically.
What Are Domain Events?
Domain events represent facts that have already occurred in the business domain and are important to domain experts. They are defined in the domain layer but are emitted from the infrastructure layer and consumed by application or domain services.
"Events that domain experts care about happening within the domain." – Vaughn Vernon, Implementing Domain‑Driven Design
Defining Domain Events
Domain events should live in the domain package and be named in the past‑tense (e.g., OrderCreated, OrderPaid). Each event must have a unique identifier and a timestamp, and it should also expose the aggregate root identifier it relates to.
Typical interface definition (illustrated in the original diagram):
Specific event interfaces can be created per aggregate, for example OrderEvent and ProductEvent, each extending the generic event interface.
Publishing Domain Events
Four main approaches are discussed:
Pass an EventPublisher as a method parameter – keeps the domain model pure but pollutes the method signature.
Use a static‑like global publisher – in Go this is simulated with a package‑level variable (e.g., var eventPublisher EventPublisher).
Return the event from the domain method – the caller decides when to publish, giving more control over transaction boundaries.
Store events temporarily in the aggregate and publish them in the repository – events are kept in a slice (e.g., OrderEvent []DomainEvent) and flushed after persistence.
Illustrative diagrams for each method are included in the original article:
The author recommends the repository‑based approach for its clear separation of concerns and testability, despite its added complexity.
Ensuring Atomicity
To guarantee that event publishing and aggregate persistence happen atomically, the article suggests storing events together with the aggregate in a single database transaction (the “event table” pattern). An asynchronous task later reads pending events, publishes them, and removes them upon success. Idempotent consumers are essential to handle possible retries.
Consuming Domain Events
Event consumption is treated like handling a special command in the application layer. A DomainEventApp struct contains methods for each event type, receiving a Context and the event, and returning an error to indicate success.
Local consumers register via RegisterSyncEventSubscriber (provided by the DDDCore library), while remote consumers rely on message queues. Because most queues guarantee at‑least‑once delivery, consumers must be idempotent—typically by checking a unique message ID before processing.
Conclusion
The article summarizes the essential characteristics of domain events: they represent immutable facts within the domain, must be named in the past tense, carry enough context for autonomous processing, and should be handled in a way that preserves transactional integrity. The next article will show how to organize a full DDD implementation across application layers.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
