IAM Service -- Event Catalog
Overview
The IAM service publishes domain events whenever a state change occurs. Events represent facts (something that happened), not intentions. They are published via a transactional outbox: the domain write and the outbox insert happen in a single database transaction, guaranteeing that every committed state change produces exactly one event record.
A background relay publishes pending outbox records to RabbitMQ. Delivery is at-least-once -- the same event may be delivered more than once. Consumers must be idempotent.
Events are only emitted on real state changes. Idempotent no-op retries (e.g., suspending an already-suspended tenant) do not produce events.
Connection Setup
Exchange
| Property | Value |
|---|---|
| Exchange name | iam.events |
| Type | topic |
| Durable | true |
| Auto-delete | false |
Queue Declaration
Consumers must declare their own durable queue and bind it to the exchange. Queue names should be unique per consuming service (e.g., billing-iam-consumer, notifications-iam-consumer).
Binding Patterns
The routing key format is {aggregate_type}.{event_type}.
| Pattern | Matches |
|---|---|
# | All events |
tenant.# | All tenant events (tenant.tenant.created, tenant.tenant.suspended, tenant.tenant.reactivated) |
tenant.tenant.created | Only tenant creation events |
membership.# | All membership events (lifecycle + role assignment) |
invitation.# | All invitation events (invitation.user.invited, invitation.invitation.accepted, invitation.invitation.revoked) |
user.# | All user events (user.user.created, user.user.suspended, user.user.reactivated) |
membership.user.role.* | Role assignment events (membership.user.role.assigned, membership.user.role.unassigned) |
realm.# | realm.realm.created |
role.# | role.role.created |
permission.# | permission.permission.created |
api_key.# | api_key.api_key.created, api_key.api_key.revoked |
Note: The routing key is constructed as {aggregate_type}.{event_type}. For events where the event type itself contains dots (e.g., user.role.assigned on the membership aggregate), the full routing key becomes membership.user.role.assigned. Use # (matches zero or more words) rather than * (matches exactly one word) when binding to aggregates with multi-segment event types.