Generate a strongly-typed, domain-scoped publisher interface and its implementation for each logical group of [Event]-annotated classes, so services can depend on a focused contract rather than the catch-all IEventPublisher.
The problem today: Every service that publishes events depends directly on IEventPublisher and calls PublishAsync<TEvent>() with the concrete event type. In larger codebases this means any service can accidentally publish any event, there are no compile-time boundaries between publishing domains, and mocking in tests requires either a full IEventPublisher mock or the TestPublisher package. There is also no IDE auto-complete surface that lists which events a particular domain is responsible for.
What we will build: An additional generator within Deveel.Events.Generators — activated by a new [EventPublisher] assembly-level or class-level attribute — that:
- Groups
[Event]-annotated classes by a configurable domain name (derived from a namespace prefix, an explicit [EventPublisher("OrderDomain")] marker, or a shared base class).
- Emits a
public interface IOrderDomainEventPublisher (name derived from the group) containing one Task PublishXxxAsync(XxxEvent @event, EventPublishOptions? options = null, CancellationToken cancellationToken = default) method per event class in the group.
- Emits a
public sealed class OrderDomainEventPublisher : IOrderDomainEventPublisher implementation that delegates each method to the injected IEventPublisher, removing all boilerplate from user code.
- Emits
AddOrderDomainEventPublisher(this IServiceCollection) DI registration extensions that register the interface and implementation as scoped services.
- Integrates with the factory generator (item 34): when both generators are active, the generated publisher methods call the compile-time
ToCloudEvent() path instead of the reflection-based factory.
Benefits:
- Domain services declare a precise dependency (
IOrderDomainEventPublisher) rather than the broad IEventPublisher, making the publishing contract explicit and auditable.
- Testing becomes trivial — mock only the three or four methods relevant to a given domain rather than the full
IEventPublisher surface.
- IDE auto-complete surfaces exactly the events a domain is authorised to publish, reducing the risk of cross-domain event leakage.
- When combined with items 34 and 35, the entire publish path from typed interface through CloudEvent creation to schema registration involves zero runtime reflection.
- Consistent with the framework-integration pattern (items 30–33), where the goal is to let teams adopt CloudEvents as a natural extension of their existing programming model rather than a separate concern.
The problem today: Every service that publishes events depends directly on
IEventPublisherand callsPublishAsync<TEvent>()with the concrete event type. In larger codebases this means any service can accidentally publish any event, there are no compile-time boundaries between publishing domains, and mocking in tests requires either a fullIEventPublishermock or theTestPublisherpackage. There is also no IDE auto-complete surface that lists which events a particular domain is responsible for.What we will build: An additional generator within
Deveel.Events.Generators— activated by a new[EventPublisher]assembly-level or class-level attribute — that:[Event]-annotated classes by a configurable domain name (derived from a namespace prefix, an explicit[EventPublisher("OrderDomain")]marker, or a shared base class).public interface IOrderDomainEventPublisher(name derived from the group) containing oneTask PublishXxxAsync(XxxEvent @event, EventPublishOptions? options = null, CancellationToken cancellationToken = default)method per event class in the group.public sealed class OrderDomainEventPublisher : IOrderDomainEventPublisherimplementation that delegates each method to the injectedIEventPublisher, removing all boilerplate from user code.AddOrderDomainEventPublisher(this IServiceCollection)DI registration extensions that register the interface and implementation as scoped services.ToCloudEvent()path instead of the reflection-based factory.Benefits:
IOrderDomainEventPublisher) rather than the broadIEventPublisher, making the publishing contract explicit and auditable.IEventPublishersurface.