diff --git a/docs/guide/handlers/discovery.md b/docs/guide/handlers/discovery.md index e50f9a355..cb4ed34ed 100644 --- a/docs/guide/handlers/discovery.md +++ b/docs/guide/handlers/discovery.md @@ -223,7 +223,7 @@ using var host = await Host.CreateDefaultBuilder() opts.DisableConventionalDiscovery(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Explicitly Ignoring Methods diff --git a/docs/guide/handlers/index.md b/docs/guide/handlers/index.md index b487700c4..1ae35ca5e 100644 --- a/docs/guide/handlers/index.md +++ b/docs/guide/handlers/index.md @@ -26,7 +26,7 @@ public class MyMessageHandler } } ``` -snippet source | anchor +snippet source | anchor 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 @@ -44,7 +44,7 @@ public static async Task publish_command(IMessageBus bus) await bus.PublishAsync(new MyMessage()); } ``` -snippet source | anchor +snippet source | anchor Between the call to `IMessageBus.PublishAsync()` and `MyMessageHandler.Handle(MyMessage)` there's a couple things @@ -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 + + +```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)); + } +} +``` +snippet source | anchor + ## Rules for Message Handlers @@ -183,7 +197,7 @@ public class ExampleHandler } } ``` -snippet source | anchor +snippet source | anchor When using instance methods, the containing handler type will be scoped to a single message and be @@ -211,7 +225,7 @@ public class ServiceUsingHandler } } ``` -snippet source | anchor +snippet source | anchor ::: tip @@ -238,7 +252,7 @@ public static class ExampleHandler } } ``` -snippet source | anchor +snippet source | anchor The handler classes can be static classes as well. This technique gets much more useful when combined with Wolverine's @@ -266,7 +280,7 @@ public static class MethodInjectionHandler } } ``` -snippet source | anchor +snippet source | anchor So, what can be injected as an argument to your message handler? @@ -377,7 +391,7 @@ public class EnvelopeUsingHandler } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/handlers/multi-tenancy.md b/docs/guide/handlers/multi-tenancy.md index 2d5578920..43bb00c27 100644 --- a/docs/guide/handlers/multi-tenancy.md +++ b/docs/guide/handlers/multi-tenancy.md @@ -37,3 +37,30 @@ public static async Task publish_by_tenant(IMessageBus bus) ``` snippet source | anchor + +## Cascading Messages + +As a convenience, you can embed tenant id information into outgoing cascading messages with these helpers: + + + +```cs +public static IEnumerable 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" + }); +``` +snippet source | anchor + diff --git a/docs/guide/runtime.md b/docs/guide/runtime.md index cc043d012..18c3a84f6 100644 --- a/docs/guide/runtime.md +++ b/docs/guide/runtime.md @@ -238,11 +238,80 @@ and agent assignments to function. The stateful, running "agents" are exposed through an `IAgent` interface like so: -snippet: sample_IAgent + + +```cs +/// +/// Models a constantly running background process within a Wolverine +/// node cluster +/// +public interface IAgent : IHostedService +{ + /// + /// Unique identification for this agent within the Wolverine system + /// + Uri Uri { get; } + + AgentStatus Status { get; } +} + +public enum AgentStatus +{ + Started, + Stopped, + Paused +} +``` +snippet source | anchor + With related groups of agents built and assigned by IoC-registered implementations of this interface: -snippet: sample_IAgentFamily + + +```cs +/// +/// Pluggable model for managing the assignment and execution of stateful, "sticky" +/// background agents on the various nodes of a running Wolverine cluster +/// +public interface IAgentFamily +{ + /// + /// Uri scheme for this family of agents + /// + string Scheme { get; } + + /// + /// List of all the possible agents by their identity for this family of agents + /// + /// + ValueTask> AllKnownAgentsAsync(); + + /// + /// Create or resolve the agent for this family + /// + /// + /// + /// + ValueTask BuildAgentAsync(Uri uri, IWolverineRuntime wolverineRuntime); + + /// + /// All supported agent uris by this node instance + /// + /// + ValueTask> SupportedAgentsAsync(); + + /// + /// Assign agents to the currently running nodes when new nodes are detected or existing + /// nodes are deactivated + /// + /// + /// + ValueTask EvaluateAssignmentsAsync(AssignmentGrid assignments); +} +``` +snippet source | anchor + Built in examples of the agent and agent family are: diff --git a/src/Samples/DocumentationSamples/using_group_ids.cs b/src/Samples/DocumentationSamples/using_group_ids.cs index a7cf8e2b9..4985e8923 100644 --- a/src/Samples/DocumentationSamples/using_group_ids.cs +++ b/src/Samples/DocumentationSamples/using_group_ids.cs @@ -25,4 +25,30 @@ public static IEnumerable Handle(IncomingMessage message) #endregion } -public record IncomingMessage; \ No newline at end of file +public record IncomingMessage; + +public class using_tenant_id +{ + #region sample_using_tenant_id_and_cascading_messages + + public static IEnumerable 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 + } + +} \ No newline at end of file diff --git a/src/Wolverine/ISendMyself.cs b/src/Wolverine/ISendMyself.cs index 6f9886c6d..5b8d2f484 100644 --- a/src/Wolverine/ISendMyself.cs +++ b/src/Wolverine/ISendMyself.cs @@ -27,6 +27,17 @@ public virtual ValueTask ApplyAsync(IMessageContext context) public static class ConfiguredMessageExtensions { + /// + /// Create a cascading message tagged to a specific tenant id + /// + /// + /// + /// + public static DeliveryMessage WithTenantId(this T message, string tenantId) + { + return new DeliveryMessage(message, new DeliveryOptions { TenantId = tenantId }); + } + /// /// Create a cascading message tagged to a specific group id ///