Class DistributedEventsDiscovery
Namespace: Momentum.ServiceDefaults.Messaging
Assembly: Momentum.ServiceDefaults.dll
public static class DistributedEventsDiscovery
Inheritance
object ← DistributedEventsDiscovery
Inherited Members
object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Methods
GetIntegrationEventTypes()
Discovers and retrieves types that represent integration event types within the application's domain assemblies.
public static IEnumerable<Type> GetIntegrationEventTypes()
Returns
Remarks
Distributed Events Discovery
Local Domain Discovery
This only applies to "local" domain assemblies
The discovery process identifies integration event types within application domain assemblies by:
- Assembly Selection: Gathering domain assemblies and the entry assembly
- Namespace Filtering: Looking for types in namespaces ending with
.IntegrationEvents
- Domain Prefix Matching: Ensuring events belong to the same domain context
Assembly Resolution Process
Assembly[] appAssemblies = [.. DomainAssemblyAttribute.GetDomainAssemblies(), ServiceDefaultsExtensions.EntryAssembly];
var domainPrefixes = appAssemblies
.Select(a => a.GetName().Name)
.Where(assemblyName => assemblyName is not null)
.Select(assemblyName =>
{
var mainNamespaceIndex = assemblyName!.IndexOf('.');
return mainNamespaceIndex >= 0 ? assemblyName[..mainNamespaceIndex] : assemblyName;
})
.ToHashSet();
Domain-Scoped Discovery
The method filters assemblies to only include those belonging to the same domain:
var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(assembly =>
{
var name = assembly.GetName().Name;
return name is not null && domainPrefixes.Any(prefix => name.StartsWith(prefix));
})
.ToArray();
Handler-Associated Events Discovery
This method identifies distributed events by analyzing handler method parameters and ensures that only events with corresponding handlers are included.
Handler Method Discovery
The discovery process examines all handler methods across domain assemblies:
Assembly[] handlerAssemblies = [.. DomainAssemblyAttribute.GetDomainAssemblies(), ServiceDefaultsExtensions.EntryAssembly];
var candidateHandlers = handlerAssemblies
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type is { IsClass: true })
.SelectMany(type => type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static));
Handler Method Identification
Methods are identified as handlers based on naming conventions:
- Handle, HandleAsync
- Handles, HandlesAsync
- Consume, ConsumeAsync
- Consumes, ConsumesAsync
private static readonly HashSet<string> HandlerMethodNames = new(StringComparer.OrdinalIgnoreCase)
{
HandlerChain.Handle,
HandlerChain.Handle + Async,
HandlerChain.Handles,
HandlerChain.Handles + Async,
HandlerChain.Consume,
HandlerChain.Consume + Async,
HandlerChain.Consumes,
HandlerChain.Consumes + Async
};
Parameter Analysis
For each valid handler method, parameters are analyzed to extract integration event types:
var integrationEvents = handlerMethods.SelectMany(method =>
method.GetParameters()
.Select(parameter => parameter.ParameterType)
.Where(IsIntegrationEventType)
).ToHashSet();
Integration Event Type Identification
Namespace-Based Detection
Integration events are identified by their namespace pattern:
private static bool IsIntegrationEventType(Type messageType) =>
messageType.Namespace?.EndsWith(IntegrationEventsNamespace) == true;
Where IntegrationEventsNamespace
is .IntegrationEvents
.
Domain Boundary Enforcement
This approach ensures that:
- Local Events Only: Only events from the same domain are discovered
- Handler Coupling: Events are only included if they have corresponding handlers
- Namespace Convention: Enforces consistent namespace organization
- Type Safety: Compile-time validation of event-handler relationships
Future Improvements
Source Generation
The discovery process could be optimized using source generation:
// TODO: Use source generation in the future for this
private static IEnumerable<MethodInfo> GetHandlerMethods()
Source generation would provide:
- Compile-time Discovery: Pre-computed event-handler mappings
- Performance Optimization: Elimination of runtime reflection
- Build-time Validation: Early detection of missing handlers
- Code Generation: Automatic registration of discovered types
GetIntegrationEventTypesWithHandlers()
Discovers and retrieves types that represent integration event types, focusing specifically on those that have associated handlers.
public static IEnumerable<Type> GetIntegrationEventTypesWithHandlers()
Returns
Remarks
Distributed Events Discovery
Local Domain Discovery
This only applies to "local" domain assemblies
The discovery process identifies integration event types within application domain assemblies by:
- Assembly Selection: Gathering domain assemblies and the entry assembly
- Namespace Filtering: Looking for types in namespaces ending with
.IntegrationEvents
- Domain Prefix Matching: Ensuring events belong to the same domain context
Assembly Resolution Process
Assembly[] appAssemblies = [.. DomainAssemblyAttribute.GetDomainAssemblies(), ServiceDefaultsExtensions.EntryAssembly];
var domainPrefixes = appAssemblies
.Select(a => a.GetName().Name)
.Where(assemblyName => assemblyName is not null)
.Select(assemblyName =>
{
var mainNamespaceIndex = assemblyName!.IndexOf('.');
return mainNamespaceIndex >= 0 ? assemblyName[..mainNamespaceIndex] : assemblyName;
})
.ToHashSet();
Domain-Scoped Discovery
The method filters assemblies to only include those belonging to the same domain:
var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(assembly =>
{
var name = assembly.GetName().Name;
return name is not null && domainPrefixes.Any(prefix => name.StartsWith(prefix));
})
.ToArray();
Handler-Associated Events Discovery
This method identifies distributed events by analyzing handler method parameters and ensures that only events with corresponding handlers are included.
Handler Method Discovery
The discovery process examines all handler methods across domain assemblies:
Assembly[] handlerAssemblies = [.. DomainAssemblyAttribute.GetDomainAssemblies(), ServiceDefaultsExtensions.EntryAssembly];
var candidateHandlers = handlerAssemblies
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type is { IsClass: true })
.SelectMany(type => type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static));
Handler Method Identification
Methods are identified as handlers based on naming conventions:
- Handle, HandleAsync
- Handles, HandlesAsync
- Consume, ConsumeAsync
- Consumes, ConsumesAsync
private static readonly HashSet<string> HandlerMethodNames = new(StringComparer.OrdinalIgnoreCase)
{
HandlerChain.Handle,
HandlerChain.Handle + Async,
HandlerChain.Handles,
HandlerChain.Handles + Async,
HandlerChain.Consume,
HandlerChain.Consume + Async,
HandlerChain.Consumes,
HandlerChain.Consumes + Async
};
Parameter Analysis
For each valid handler method, parameters are analyzed to extract integration event types:
var integrationEvents = handlerMethods.SelectMany(method =>
method.GetParameters()
.Select(parameter => parameter.ParameterType)
.Where(IsIntegrationEventType)
).ToHashSet();
Integration Event Type Identification
Namespace-Based Detection
Integration events are identified by their namespace pattern:
private static bool IsIntegrationEventType(Type messageType) =>
messageType.Namespace?.EndsWith(IntegrationEventsNamespace) == true;
Where IntegrationEventsNamespace
is .IntegrationEvents
.
Domain Boundary Enforcement
This approach ensures that:
- Local Events Only: Only events from the same domain are discovered
- Handler Coupling: Events are only included if they have corresponding handlers
- Namespace Convention: Enforces consistent namespace organization
- Type Safety: Compile-time validation of event-handler relationships
Future Improvements
Source Generation
The discovery process could be optimized using source generation:
// TODO: Use source generation in the future for this
private static IEnumerable<MethodInfo> GetHandlerMethods()
Source generation would provide:
- Compile-time Discovery: Pre-computed event-handler mappings
- Performance Optimization: Elimination of runtime reflection
- Build-time Validation: Early detection of missing handlers
- Code Generation: Automatic registration of discovered types