Skip to content

Commit de3d8f9

Browse files
committed
refactor: replace cross-assembly internal sharing with same-assembly-only dedup
1 parent 4aa5203 commit de3d8f9

7 files changed

Lines changed: 67 additions & 129 deletions

File tree

src/Vulthil.Messaging.Outbox/BrokerOutboxDispatcher.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ internal sealed class BrokerOutboxDispatcher(
3030
&& method.IsGenericMethodDefinition
3131
&& method.GetParameters().Length == 3);
3232

33+
private static readonly ConcurrentDictionary<string, Type> TypeCache = [];
3334
private static readonly ConcurrentDictionary<Type, MethodInfo> PublishByType = [];
3435
private static readonly ConcurrentDictionary<Type, MethodInfo> SendByType = [];
3536

@@ -38,7 +39,7 @@ public bool Handles(OutboxDestination destination) =>
3839

3940
public async Task DispatchAsync(OutboxMessageData message, CancellationToken cancellationToken)
4041
{
41-
var messageType = OutboxMessageTypeResolver.Resolve(message.Type, "message");
42+
var messageType = ResolveType(message.Type);
4243
var payload = JsonSerializer.Deserialize(message.Content, messageType, messageConfigurationProvider.JsonSerializerOptions)!;
4344
var metadata = message.Metadata is null
4445
? null
@@ -92,4 +93,16 @@ private static void Apply(BrokerOutboxMetadata? metadata, IPublishContext contex
9293
context.AddHeaders(headers);
9394
}
9495
}
96+
97+
private static Type ResolveType(string typeName) => TypeCache.GetOrAdd(typeName, static name =>
98+
{
99+
var type = Type.GetType(name)
100+
?? AppDomain.CurrentDomain.GetAssemblies()
101+
.Select(assembly => assembly.GetType(name))
102+
.FirstOrDefault(found => found is not null);
103+
104+
return type ?? throw new InvalidOperationException(
105+
$"Unable to resolve the message type '{name}' for an outbox relay. " +
106+
"Ensure the assembly that defines the type is loaded in the relay process.");
107+
});
95108
}

src/Vulthil.Messaging.RabbitMq/Consumers/MessageContextFactory.cs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,28 @@ private static MessageContext<TMessage> BuildTypedMetadata<TMessage>(
9191
CancellationToken cancellationToken)
9292
{
9393
var metadata = ExtractMetadata(ea);
94-
return MessageContext.BuildTyped(
95-
message,
96-
publisher,
97-
sendEndpointProvider,
98-
metadata.MessageId,
99-
metadata.CorrelationId,
100-
metadata.RequestId,
101-
metadata.RoutingKey,
102-
metadata.Headers,
103-
metadata.Redelivered,
104-
metadata.RetryCount,
105-
metadata.ConversationId,
106-
metadata.InitiatorId,
107-
metadata.SourceAddress,
108-
metadata.DestinationAddress,
109-
metadata.ResponseAddress,
110-
metadata.FaultAddress,
111-
metadata.SentTime,
112-
metadata.ExpirationTime,
113-
cancellationToken);
94+
return new MessageContext<TMessage>
95+
{
96+
Message = message,
97+
Publisher = publisher,
98+
SendEndpointProvider = sendEndpointProvider,
99+
CancellationToken = cancellationToken,
100+
MessageId = metadata.MessageId,
101+
CorrelationId = metadata.CorrelationId,
102+
RequestId = metadata.RequestId,
103+
RoutingKey = metadata.RoutingKey,
104+
Headers = metadata.Headers,
105+
Redelivered = metadata.Redelivered,
106+
RetryCount = metadata.RetryCount,
107+
ConversationId = metadata.ConversationId,
108+
InitiatorId = metadata.InitiatorId,
109+
SourceAddress = metadata.SourceAddress,
110+
DestinationAddress = metadata.DestinationAddress,
111+
ResponseAddress = metadata.ResponseAddress,
112+
FaultAddress = metadata.FaultAddress,
113+
SentTime = metadata.SentTime,
114+
ExpirationTime = metadata.ExpirationTime,
115+
};
114116
}
115117

116118
/// <summary>

src/Vulthil.Messaging/Transport/MessageContext.cs

Lines changed: 15 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -110,76 +110,30 @@ public static MessageContext<TMessage> CreateFromEnvelope<TMessage>(
110110
userHeaders[key] = HeaderValueNormalizer.Normalize(value);
111111
}
112112

113-
return BuildTyped(
114-
message,
115-
publisher,
116-
sendEndpointProvider,
117-
envelope.MessageId,
118-
envelope.CorrelationId ?? string.Empty,
119-
envelope.RequestId ?? envelope.CorrelationId,
120-
routingKey,
121-
userHeaders,
122-
redelivered,
123-
retryCount,
124-
envelope.ConversationId,
125-
envelope.InitiatorId,
126-
ParseAddress(envelope.SourceAddress),
127-
ParseAddress(envelope.DestinationAddress),
128-
ParseAddress(envelope.ResponseAddress)
129-
?? (string.IsNullOrEmpty(replyToFallback) ? null : new Uri($"queue:{replyToFallback}")),
130-
ParseAddress(envelope.FaultAddress),
131-
envelope.SentTime,
132-
envelope.ExpirationTime,
133-
cancellationToken);
134-
}
135-
136-
/// <summary>
137-
/// Constructs a live typed <see cref="MessageContext{TMessage}"/> from already-resolved metadata. Shared by
138-
/// <see cref="CreateFromEnvelope{TMessage}"/> (the standard envelope receive path) and the RabbitMQ transport's
139-
/// bare-JSON compat path, which resolve these values from their own wire representations — a parsed
140-
/// <see cref="MessageEnvelope"/> versus raw AMQP properties/headers — before delegating the final mapping here.
141-
/// </summary>
142-
internal static MessageContext<TMessage> BuildTyped<TMessage>(
143-
TMessage message,
144-
IPublisher? publisher,
145-
ISendEndpointProvider? sendEndpointProvider,
146-
string? messageId,
147-
string? correlationId,
148-
string? requestId,
149-
string routingKey,
150-
IReadOnlyDictionary<string, object?> headers,
151-
bool redelivered,
152-
int retryCount,
153-
string? conversationId,
154-
string? initiatorId,
155-
Uri? sourceAddress,
156-
Uri? destinationAddress,
157-
Uri? responseAddress,
158-
Uri? faultAddress,
159-
DateTimeOffset? sentTime,
160-
DateTimeOffset? expirationTime,
161-
CancellationToken cancellationToken) => new MessageContext<TMessage>
113+
return new MessageContext<TMessage>
162114
{
163115
Message = message,
164116
Publisher = publisher,
165117
SendEndpointProvider = sendEndpointProvider,
166118
CancellationToken = cancellationToken,
167-
MessageId = messageId,
168-
CorrelationId = correlationId,
169-
RequestId = requestId,
119+
MessageId = envelope.MessageId,
120+
CorrelationId = envelope.CorrelationId ?? string.Empty,
121+
RequestId = envelope.RequestId ?? envelope.CorrelationId,
170122
RoutingKey = routingKey,
171-
Headers = headers,
123+
Headers = userHeaders,
172124
Redelivered = redelivered,
173125
RetryCount = retryCount,
174-
ConversationId = conversationId,
175-
InitiatorId = initiatorId,
176-
SourceAddress = sourceAddress,
177-
DestinationAddress = destinationAddress,
178-
ResponseAddress = responseAddress,
179-
FaultAddress = faultAddress,
180-
SentTime = sentTime,
181-
ExpirationTime = expirationTime,
126+
ConversationId = envelope.ConversationId,
127+
InitiatorId = envelope.InitiatorId,
128+
SourceAddress = ParseAddress(envelope.SourceAddress),
129+
DestinationAddress = ParseAddress(envelope.DestinationAddress),
130+
ResponseAddress = ParseAddress(envelope.ResponseAddress)
131+
?? (string.IsNullOrEmpty(replyToFallback) ? null : new Uri($"queue:{replyToFallback}")),
132+
FaultAddress = ParseAddress(envelope.FaultAddress),
133+
SentTime = envelope.SentTime,
134+
ExpirationTime = envelope.ExpirationTime,
182135
};
136+
}
183137

184138
/// <summary>
185139
/// Auto-propagates correlation metadata (correlation, conversation, initiator) from this incoming context onto

src/Vulthil.Messaging/Vulthil.Messaging.csproj

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,4 @@
1717
<ProjectReference Include="..\Vulthil.Messaging.Abstractions\Vulthil.Messaging.Abstractions.csproj" />
1818
</ItemGroup>
1919

20-
<ItemGroup>
21-
<!-- The RabbitMQ transport's bare-JSON compat receive path shares MessageContext's internal typed-context
22-
builder instead of duplicating the envelope-receive mapping. -->
23-
<InternalsVisibleTo Include="Vulthil.Messaging.RabbitMq" />
24-
</ItemGroup>
25-
2620
</Project>

src/Vulthil.SharedKernel.Outbox/DomainEventOutboxDispatcher.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Concurrent;
12
using System.Text.Json;
23
using Vulthil.SharedKernel.Application.Messaging.DomainEvents;
34

@@ -10,13 +11,27 @@ namespace Vulthil.SharedKernel.Outbox;
1011
/// </summary>
1112
internal sealed class DomainEventOutboxDispatcher(IDomainEventPublisher domainEventPublisher) : IOutboxDispatcher
1213
{
14+
private static readonly ConcurrentDictionary<string, Type> _typeCache = [];
15+
1316
public bool Handles(OutboxDestination destination) => destination == OutboxDestination.DomainEvent;
1417

1518
public async Task DispatchAsync(OutboxMessageData message, CancellationToken cancellationToken)
1619
{
17-
var messageType = OutboxMessageTypeResolver.Resolve(message.Type, "domain-event");
20+
var messageType = GetOrAddMessageType(message.Type);
1821
var domainEvent = JsonSerializer.Deserialize(message.Content, messageType)!;
1922

2023
await domainEventPublisher.PublishAsync(domainEvent, cancellationToken);
2124
}
25+
26+
private static Type GetOrAddMessageType(string typeName) => _typeCache.GetOrAdd(typeName, static t =>
27+
{
28+
var type = Type.GetType(t)
29+
?? AppDomain.CurrentDomain.GetAssemblies()
30+
.Select(a => a.GetType(t))
31+
.FirstOrDefault(found => found is not null);
32+
33+
return type ?? throw new InvalidOperationException(
34+
$"Unable to resolve the domain-event type '{t}' for an outbox relay. " +
35+
"Ensure the assembly that defines the type is loaded in the relay process.");
36+
});
2237
}

src/Vulthil.SharedKernel.Outbox/OutboxMessageTypeResolver.cs

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/Vulthil.SharedKernel.Outbox/Vulthil.SharedKernel.Outbox.csproj

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,4 @@
1818
<ProjectReference Include="..\Vulthil.SharedKernel.Application\Vulthil.SharedKernel.Application.csproj" />
1919
</ItemGroup>
2020

21-
<ItemGroup>
22-
<!-- Vulthil.Messaging.Outbox's BrokerOutboxDispatcher shares OutboxMessageTypeResolver with this
23-
assembly's DomainEventOutboxDispatcher instead of duplicating the type-resolution cache. -->
24-
<InternalsVisibleTo Include="Vulthil.Messaging.Outbox" />
25-
</ItemGroup>
26-
2721
</Project>

0 commit comments

Comments
 (0)