profileShare

rasmusjy / splitapp-backend-microservices

Read-only snapshot

No repository description.

main default branch 501 files Expires Sep 13, 2026, 9:06 AM
ServiceCollectionExtensions.cs 4,010 bytes
1 using Microsoft.Extensions.Configuration;
2 using Microsoft.Extensions.DependencyInjection;
3 using Microsoft.Extensions.DependencyInjection.Extensions;
4 using SplitApp.Shared.Messaging.Integration.Users;
5 using SplitApp.Shared.Messaging.Internal;
6
7 namespace SplitApp.Shared.Messaging;
8
9 public static class ServiceCollectionExtensions
10 {
11 /// <summary>
12 /// Registers the RabbitMQ-backed message bus, connection provider, consumer hosted service,
13 /// and handler registry. Bind <see cref="MessagingOptions"/> from configuration section
14 /// <c>Messaging:RabbitMq</c>. Call AddIntegrationEventHandler / AddIntegrationRequestHandler
15 /// for each handler the service should consume.
16 /// </summary>
17 public static IServiceCollection AddMessaging(this IServiceCollection services, IConfiguration configuration)
18 {
19 services.AddOptions<MessagingOptions>()
20 .Bind(configuration.GetSection(MessagingOptions.SectionName))
21 .PostConfigure(o =>
22 {
23 if (string.IsNullOrWhiteSpace(o.ServiceName))
24 throw new InvalidOperationException("MessagingOptions.ServiceName must be set per host");
25 });
26
27 services.TryAddSingleton<RabbitMqConnectionProvider>();
28
29 // HandlerRegistry pulls in every MessageHandlerRegistration singleton registered
30 // via AddIntegrationEventHandler / AddIntegrationRequestHandler. Resolved as singleton
31 // so the consumer service sees all registrations.
32 services.TryAddSingleton<HandlerRegistry>(sp =>
33 {
34 var registry = new HandlerRegistry();
35 foreach (var r in sp.GetServices<MessageHandlerRegistration>())
36 {
37 registry.Register(r);
38 }
39 return registry;
40 });
41
42 services.TryAddSingleton<IMessageBus, RabbitMqBus>();
43 services.TryAddSingleton<IUserLookup, UserLookup>();
44 services.AddHostedService<RabbitMqConsumerHostedService>();
45
46 return services;
47 }
48
49 /// <summary>Convenience overload that sets <see cref="MessagingOptions.ServiceName"/> in-line.</summary>
50 public static IServiceCollection AddMessaging(
51 this IServiceCollection services,
52 IConfiguration configuration,
53 string serviceName)
54 {
55 services.AddMessaging(configuration);
56 services.PostConfigure<MessagingOptions>(o => o.ServiceName = serviceName);
57 return services;
58 }
59
60 public static IServiceCollection AddIntegrationEventHandler<TEvent, THandler>(this IServiceCollection services)
61 where TEvent : IIntegrationEvent
62 where THandler : class, IEventHandler<TEvent>
63 {
64 services.AddScoped<THandler>();
65
66 services.AddSingleton(_ => new MessageHandlerRegistration
67 {
68 MessageType = typeof(TEvent),
69 HandlerType = typeof(THandler),
70 ResponseType = null,
71 Exchange = RabbitMqTopology.EventsExchange,
72 RoutingKey = typeof(TEvent).Name,
73 Kind = MessageKind.Event,
74 Dispatcher = MessageDispatcherFactory.CreateEventDispatcher<TEvent, THandler>(),
75 });
76
77 return services;
78 }
79
80 public static IServiceCollection AddIntegrationRequestHandler<TRequest, TResponse, THandler>(
81 this IServiceCollection services)
82 where TRequest : IIntegrationRequest<TResponse>
83 where THandler : class, IRequestHandler<TRequest, TResponse>
84 {
85 services.AddScoped<THandler>();
86
87 services.AddSingleton(_ => new MessageHandlerRegistration
88 {
89 MessageType = typeof(TRequest),
90 HandlerType = typeof(THandler),
91 ResponseType = typeof(TResponse),
92 Exchange = RabbitMqTopology.RequestsExchange,
93 RoutingKey = typeof(TRequest).Name,
94 Kind = MessageKind.Request,
95 Dispatcher = MessageDispatcherFactory.CreateRequestDispatcher<TRequest, TResponse, THandler>(),
96 });
97
98 return services;
99 }
100 }
101