Skip to content

Commit

Permalink
convenience method for cascaded messages with tenant id. Closes GH-1071
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremydmiller committed Oct 15, 2024
1 parent 588b37a commit afdb370
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 12 deletions.
2 changes: 1 addition & 1 deletion docs/guide/handlers/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ using var host = await Host.CreateDefaultBuilder()
opts.DisableConventionalDiscovery();
}).StartAsync();
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L205-L214' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_explicithandlerdiscovery' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L227-L236' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_explicithandlerdiscovery' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Explicitly Ignoring Methods
Expand Down
30 changes: 22 additions & 8 deletions docs/guide/handlers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class MyMessageHandler
}
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L69-L79' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_simplest_possible_handler' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L91-L101' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_simplest_possible_handler' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

If you've used other messaging, command execution, or so-called "mediator" tools in .NET, you'll surely notice the absence of any kind of
Expand All @@ -44,7 +44,7 @@ public static async Task publish_command(IMessageBus bus)
await bus.PublishAsync(new MyMessage());
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L83-L90' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_publish_mymessage' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L105-L112' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_publish_mymessage' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Between the call to `IMessageBus.PublishAsync()` and `MyMessageHandler.Handle(MyMessage)` there's a couple things
Expand Down Expand Up @@ -116,7 +116,21 @@ public class ValidMessageHandlers

It's also valid to use class instances with constructor arguments for your handlers:

snippet: sample_instance_handler
<!-- snippet: sample_instance_handler -->
<a id='snippet-sample_instance_handler'></a>
```cs
// Wolverine does constructor injection as you're probably
// used to with basically every other framework in .NET
public class CreateProjectHandler(IProjectRepository Repository)
{
public async Task HandleAsync(CreateProject message)
{
await Repository.CreateAsync(new Project(message.Name));
}
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L72-L84' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_instance_handler' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Rules for Message Handlers

Expand Down Expand Up @@ -183,7 +197,7 @@ public class ExampleHandler
}
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L95-L111' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_examplehandlerbyinstance' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L117-L133' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_examplehandlerbyinstance' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

When using instance methods, the containing handler type will be scoped to a single message and be
Expand Down Expand Up @@ -211,7 +225,7 @@ public class ServiceUsingHandler
}
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L139-L159' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_handlerbuiltbyconstructorinjection' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L161-L181' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_handlerbuiltbyconstructorinjection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

::: tip
Expand All @@ -238,7 +252,7 @@ public static class ExampleHandler
}
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L116-L132' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_examplehandlerbystaticmethods' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L138-L154' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_examplehandlerbystaticmethods' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The handler classes can be static classes as well. This technique gets much more useful when combined with Wolverine's
Expand Down Expand Up @@ -266,7 +280,7 @@ public static class MethodInjectionHandler
}
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L166-L179' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_handlerusingmethodinjection' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L188-L201' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_handlerusingmethodinjection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

So, what can be injected as an argument to your message handler?
Expand Down Expand Up @@ -377,7 +391,7 @@ public class EnvelopeUsingHandler
}
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L182-L193' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_handlerusingenvelope' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/HandlerExamples.cs#L204-L215' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_handlerusingenvelope' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down
27 changes: 27 additions & 0 deletions docs/guide/handlers/multi-tenancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,30 @@ public static async Task publish_by_tenant(IMessageBus bus)
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/MultiTenantedTodoService/MultiTenantedTodoWebService.Tests/end_to_end.cs#L110-L118' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_publish_by_tenant' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Cascading Messages

As a convenience, you can embed tenant id information into outgoing cascading messages with these helpers:

<!-- snippet: sample_using_tenant_id_and_cascading_messages -->
<a id='snippet-sample_using_tenant_id_and_cascading_messages'></a>
```cs
public static IEnumerable<object> Handle(IncomingMessage message)
{
yield return new Message1().WithTenantId("one");
yield return new Message2().WithTenantId("one");

yield return new Message3().WithDeliveryOptions(new DeliveryOptions
{
ScheduleDelay = 5.Minutes(),
TenantId = "two"
});

// Long hand
yield return new Message4().WithDeliveryOptions(new DeliveryOptions
{
TenantId = "one"
});
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Samples/DocumentationSamples/using_group_ids.cs#L32-L51' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_using_tenant_id_and_cascading_messages' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
73 changes: 71 additions & 2 deletions docs/guide/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,80 @@ and agent assignments to function.
The stateful, running "agents" are exposed through an `IAgent`
interface like so:

snippet: sample_IAgent
<!-- snippet: sample_IAgent -->
<a id='snippet-sample_iagent'></a>
```cs
/// <summary>
/// Models a constantly running background process within a Wolverine
/// node cluster
/// </summary>
public interface IAgent : IHostedService
{
/// <summary>
/// Unique identification for this agent within the Wolverine system
/// </summary>
Uri Uri { get; }

AgentStatus Status { get; }
}

public enum AgentStatus
{
Started,
Stopped,
Paused
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Agents/IAgent.cs#L6-L29' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_iagent' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

With related groups of agents built and assigned by IoC-registered implementations of this interface:

snippet: sample_IAgentFamily
<!-- snippet: sample_IAgentFamily -->
<a id='snippet-sample_iagentfamily'></a>
```cs
/// <summary>
/// Pluggable model for managing the assignment and execution of stateful, "sticky"
/// background agents on the various nodes of a running Wolverine cluster
/// </summary>
public interface IAgentFamily
{
/// <summary>
/// Uri scheme for this family of agents
/// </summary>
string Scheme { get; }

/// <summary>
/// List of all the possible agents by their identity for this family of agents
/// </summary>
/// <returns></returns>
ValueTask<IReadOnlyList<Uri>> AllKnownAgentsAsync();

/// <summary>
/// Create or resolve the agent for this family
/// </summary>
/// <param name="uri"></param>
/// <param name="wolverineRuntime"></param>
/// <returns></returns>
ValueTask<IAgent> BuildAgentAsync(Uri uri, IWolverineRuntime wolverineRuntime);

/// <summary>
/// All supported agent uris by this node instance
/// </summary>
/// <returns></returns>
ValueTask<IReadOnlyList<Uri>> SupportedAgentsAsync();

/// <summary>
/// Assign agents to the currently running nodes when new nodes are detected or existing
/// nodes are deactivated
/// </summary>
/// <param name="assignments"></param>
/// <returns></returns>
ValueTask EvaluateAssignmentsAsync(AssignmentGrid assignments);
}
```
<sup><a href='https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Agents/IAgentFamily.cs#L16-L58' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_iagentfamily' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Built in examples of the agent and agent family are:

Expand Down
28 changes: 27 additions & 1 deletion src/Samples/DocumentationSamples/using_group_ids.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,30 @@ public static IEnumerable<object> Handle(IncomingMessage message)
#endregion
}

public record IncomingMessage;
public record IncomingMessage;

public class using_tenant_id
{
#region sample_using_tenant_id_and_cascading_messages

public static IEnumerable<object> Handle(IncomingMessage message)
{
yield return new Message1().WithTenantId("one");
yield return new Message2().WithTenantId("one");

yield return new Message3().WithDeliveryOptions(new DeliveryOptions
{
ScheduleDelay = 5.Minutes(),
TenantId = "two"
});

// Long hand
yield return new Message4().WithDeliveryOptions(new DeliveryOptions
{
TenantId = "one"
});

#endregion
}

}
11 changes: 11 additions & 0 deletions src/Wolverine/ISendMyself.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ public virtual ValueTask ApplyAsync(IMessageContext context)

public static class ConfiguredMessageExtensions
{
/// <summary>
/// Create a cascading message tagged to a specific tenant id
/// </summary>
/// <param name="message"></param>
/// <param name="tenantId"></param>
/// <returns></returns>
public static DeliveryMessage<T> WithTenantId<T>(this T message, string tenantId)
{
return new DeliveryMessage<T>(message, new DeliveryOptions { TenantId = tenantId });
}

/// <summary>
/// Create a cascading message tagged to a specific group id
/// </summary>
Expand Down

0 comments on commit afdb370

Please sign in to comment.