Skip to content

Commit

Permalink
Changed the behavior of InvokeAsync<T>() such that the T is NOT sent …
Browse files Browse the repository at this point in the history
…as a cascading message and only as the response. Closes GH-1024
  • Loading branch information
jeremydmiller committed Sep 3, 2024
1 parent 1aa62cc commit bde3c53
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 9 deletions.
10 changes: 10 additions & 0 deletions docs/guide/messaging/message-bus.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ case, Wolverine does enforce timeout conditions with a default of 5 seconds whic

## Request/Reply

::: warning
There was a breaking change in behavior for this functionality in Wolverine 3.0. The response type, the `T` in `InvokeAsync<T>()` is **not**
sent as a cascaded message if that type is the requested response type. You will have to explicitly send the response out through `IMessageBus.PublishAsync()`
to force that to be sent out instead of just being the response.
:::

Wolverine also has direct support for the [request/reply](https://www.enterpriseintegrationpatterns.com/RequestReply.html) pattern or really just mediating between your code and complex query handlers through
the `IMessageBus.InvokeAsync<T>()` API. To make that concrete, let's assume you want to request the results of a mathematical operation as shown below
in these message types and a corresponding message handler:
Expand Down Expand Up @@ -127,6 +133,10 @@ public async Task invoke_math_operations(IMessageBus bus)
Note that this API hides whether or not this operation is a local operation running on the same thread and invoking a local message handler or sending a message through to a remote
endpoint and waiting for the response. The same timeout mechanics and performance concerns apply to this operation as the `InvokeAsync()` method described in the previous section.

Note that if you execute the `Numbers` message from above with `InvokeAsync<Results>()`, the `Results` response will only be
returned as the response and will not be published as a message. This was a breaking change in Wolverine 3.0. We think (hope)
that this will be less confusing.

## Sending or Publishing Messages

[Publish/Subscribe](https://docs.microsoft.com/en-us/azure/architecture/patterns/publisher-subscriber) is a messaging pattern where the senders of messages do not need to specifically know what the specific subscribers are for a given message. In this case, some kind of middleware or infrastructure is responsible for either allowing subscribers to express interest in what messages they need to receive or apply routing rules to send the published messages to the right places. Wolverine's messaging support was largely built to support the publish/subscribe messaging pattern.
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,7 @@ builder.Services.AddWolverineHttp();
<!-- endSnippet -->

Also for Wolverine.Http users, the `[Document]` attribute behavior in the Marten integration is now "required by default."

The behavior of `IMessageBus.InvokeAsync<T>(message)` changed in 3.0 such that the `T` response **is not also published as a
message** at the same time when the initial message is sent with request/response semantics. Wolverine has gone back and forth
in this behavior in its life, but at this point, the Wolverine thinks that this is the least confusing behavioral rule.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

namespace CoreTests.Acceptance;

public class local_invoke_also_publishes_the_return_value
public class local_invoke_does_not_publish_the_return_value
{
[Fact]
public async Task should_also_publish_the_return_value_when_invoking_locally()
public async Task should_not_publish_the_return_value_when_invoking_locally()
{
using var host = await Host.CreateDefaultBuilder()
.UseWolverine().StartAsync();
Expand All @@ -18,11 +18,7 @@ public async Task should_also_publish_the_return_value_when_invoking_locally()

response.Name.ShouldBe(name);

tracked.Sent.SingleMessage<CommandInvoked>()
.Name.ShouldBe(name);

tracked.Sent.SingleMessage<Message1>().Id.ShouldBe(response.Id);
tracked.Sent.SingleMessage<Message2>().Id.ShouldBe(response.Id);
tracked.Sent.MessagesOf<CommandInvoked>().Any().ShouldBeFalse();
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/Wolverine/Runtime/Handlers/Executor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ public async Task<T> InvokeAsync<T>(object message, MessageBus bus, Cancellation
ReplyUri = TransportConstants.RepliesUri,
ReplyRequested = typeof(T).ToMessageTypeName(),
ResponseType = typeof(T),
TenantId = tenantId ?? bus.TenantId
TenantId = tenantId ?? bus.TenantId,
DoNotCascadeResponse = true
};

bus.TrackEnvelopeCorrelation(envelope, Activity.Current);
Expand Down
2 changes: 1 addition & 1 deletion src/Wolverine/Runtime/MessageContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ public async Task EnqueueCascadingAsync(object? message)
}

if (Envelope?.ResponseType != null && (message?.GetType() == Envelope.ResponseType ||
Envelope.ResponseType.IsAssignableFrom(message?.GetType())))
Envelope.ResponseType.IsInstanceOfType(message)))
{
Envelope.Response = message;

Expand Down

0 comments on commit bde3c53

Please sign in to comment.