DDD: Domain Events — Facts Your Domain Announces
Immutable facts that happened in your domain, named in past tense, published after a state change commits.
When to use
- Notifying other bounded contexts of state changes without coupling
- Triggering side effects (emails, projections, integrations)
- Building an audit trail
Tradeoffs
- Eventual consistency — consumers lag behind the write
- Event schema becomes a contract between contexts (coupling at the schema level)
- Go
- Python
type OrderPlaced struct {
OrderID string
CustomerID string
OccurredAt time.Time
}
type EventPublisher interface {
Publish(event any) error
}
func (o *Order) Place(pub EventPublisher) error {
o.Status = "placed"
// publish AFTER state change — caller commits DB tx first
return pub.Publish(OrderPlaced{
OrderID: o.ID, CustomerID: o.CustomerID, OccurredAt: time.Now(),
})
}
from dataclasses import dataclass
from datetime import datetime
from typing import Protocol
@dataclass(frozen=True)
class OrderPlaced:
order_id: str
customer_id: str
occurred_at: datetime
class EventPublisher(Protocol):
def publish(self, event: object) -> None: ...
def place_order(order: Order, pub: EventPublisher) -> None:
order.status = "placed"
# publish AFTER state change — caller commits DB tx first
pub.publish(OrderPlaced(order.id, order.customer_id, datetime.utcnow()))
Gotcha: Publish events AFTER the transaction commits — not inside it. Otherwise you publish events for transactions that roll back.