Skip to content

Commit cd33c63

Browse files
committed
Ability to use messages to sagas even when the message is also handled by separate handlers otherwise.
1 parent 6b9fa64 commit cd33c63

File tree

5 files changed

+93
-10
lines changed

5 files changed

+93
-10
lines changed

docs/guide/handlers/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ would use those values.
165165
Pay attention to this section if you are trying to utilize a "Modular Monolith" architecture.
166166
:::
167167

168-
::: warning
169-
The `Separated` setting is ignored by `Saga` handlers
168+
::: info
169+
The `Separated` setting is useful even with `Saga` handlers as of Wolverine 5.10, but ignored
170+
in previous versions.
170171
:::
171172

172173
Let's say that you want to take more than one action on a message type published in or to your
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Diagnostics;
2+
using JasperFx.Core;
3+
using Microsoft.Extensions.Hosting;
4+
using Wolverine.Attributes;
5+
using Wolverine.Tracking;
6+
using Xunit;
7+
8+
namespace CoreTests.Persistence.Sagas;
9+
10+
public class using_a_saga_with_separated_behavior_mode
11+
{
12+
[Fact]
13+
public async Task able_to_use_separated_behaviors_with_sagas()
14+
{
15+
using var host = await Host.CreateDefaultBuilder()
16+
.UseWolverine(opts =>
17+
{
18+
opts.Discovery.DisableConventionalDiscovery()
19+
.IncludeType(typeof(TrackedThing))
20+
.IncludeType(typeof(OtherThingUpdatedHandler));
21+
22+
opts.MultipleHandlerBehavior = MultipleHandlerBehavior.Separated;
23+
}).StartAsync();
24+
25+
var id = Guid.NewGuid();
26+
27+
await host.InvokeMessageAndWaitAsync(new StartTracking(id));
28+
29+
var tracked = await host.SendMessageAndWaitAsync(new ThingUpdated(id));
30+
var envelopes = tracked.Executed.Envelopes().Where(x => x.Message is ThingUpdated).ToArray();
31+
envelopes.Length.ShouldBe(2);
32+
33+
envelopes.Any(x => x.Destination == new Uri("local://coretests.persistence.sagas.trackedthing/")).ShouldBeTrue();
34+
envelopes.Any(x => x.Destination == new Uri("local://coretests.persistence.sagas.otherthingupdatedhandler/")).ShouldBeTrue();
35+
}
36+
}
37+
38+
public record StartTracking(Guid Id);
39+
40+
public record ThingUpdated(Guid Id);
41+
42+
public class TrackedThing : Saga
43+
{
44+
public Guid Id { get; set; }
45+
46+
public int Updates { get; set; }
47+
48+
public static TrackedThing Start(StartTracking cmd) => new TrackedThing { Id = cmd.Id };
49+
50+
public void Handle(ThingUpdated updated, Envelope envelope)
51+
{
52+
Updates++;
53+
Debug.WriteLine(envelope.Destination);
54+
}
55+
}
56+
57+
[WolverineIgnore]
58+
public static class OtherThingUpdatedHandler
59+
{
60+
public static void Handle(ThingUpdated updated)
61+
{
62+
Debug.WriteLine("Got updated for " + updated.Id);
63+
}
64+
}

src/Wolverine/Persistence/Sagas/SagaChain.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
using System.Diagnostics;
12
using JasperFx;
23
using JasperFx.CodeGeneration;
34
using JasperFx.CodeGeneration.Frames;
45
using JasperFx.CodeGeneration.Model;
56
using JasperFx.Core;
67
using JasperFx.Core.Reflection;
78
using System.Reflection;
9+
using Wolverine.Configuration;
810
using Wolverine.Logging;
911
using Wolverine.Runtime.Handlers;
1012

@@ -51,6 +53,25 @@ public SagaChain(WolverineOptions options, IGrouping<Type, HandlerCall> grouping
5153
}
5254
}
5355

56+
public SagaChain(HandlerCall handlerCall, HandlerGraph handlerGraph, Endpoint[] endpoints) : base(handlerCall, handlerGraph)
57+
{
58+
foreach (var endpoint in endpoints) RegisterEndpoint(endpoint);
59+
60+
var saga = handlerCall;
61+
SagaType = saga.HandlerType;
62+
SagaMethodInfo = saga.Method;
63+
64+
Handlers.Add(handlerCall);
65+
66+
SagaIdMember = DetermineSagaIdMember(MessageType, SagaType, saga.Method);
67+
68+
// Automatically audit the saga id
69+
if (SagaIdMember != null && AuditedMembers.All(x => x.Member != SagaIdMember))
70+
{
71+
AuditedMembers.Add(new AuditedMember(SagaIdMember, SagaIdMember.Name, SagaIdMember.Name));
72+
}
73+
}
74+
5475
public override bool TryInferMessageIdentity(out PropertyInfo? property)
5576
{
5677
property = SagaIdMember as PropertyInfo;
@@ -62,11 +83,6 @@ protected override void validateAgainstInvalidSagaMethods(IGrouping<Type, Handle
6283
// Nothing
6384
}
6485

65-
protected override void tryAssignStickyEndpoints(HandlerCall handlerCall, WolverineOptions options)
66-
{
67-
// nope, don't do this with saga chains
68-
}
69-
7086
public Type SagaType { get; }
7187

7288
public MethodInfo? SagaMethodInfo { get; set; }

src/Wolverine/Runtime/Handlers/HandlerChain.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public HandlerChain(Type messageType, HandlerGraph parent)
7070
applyAuditAttributes(messageType);
7171
}
7272

73-
private HandlerChain(MethodCall call, HandlerGraph parent) : this(call.Method.MessageType()!, parent)
73+
protected HandlerChain(MethodCall call, HandlerGraph parent) : this(call.Method.MessageType()!, parent)
7474
{
7575
Handlers.Add(call);
7676
}
@@ -284,15 +284,16 @@ bool ICodeFile.AttachTypesSynchronously(GenerationRules rules, Assembly assembly
284284
/// </summary>
285285
public FailureRuleCollection Failures { get; } = new();
286286

287-
protected virtual void tryAssignStickyEndpoints(HandlerCall handlerCall, WolverineOptions options)
287+
protected void tryAssignStickyEndpoints(HandlerCall handlerCall, WolverineOptions options)
288288
{
289289
var endpoints = findStickyEndpoints(handlerCall, options).Distinct().ToArray();
290290
if (endpoints.Any())
291291
{
292292
foreach (var stub in endpoints.OfType<StubEndpoint>())
293293
stub.Subscriptions.Add(Subscription.ForType(MessageType));
294294

295-
var chain = new HandlerChain(handlerCall, options.HandlerGraph, endpoints);
295+
var chain = handlerCall.HandlerType.CanBeCastTo<Saga>() ? new SagaChain(handlerCall,
296+
options.HandlerGraph, endpoints) : new HandlerChain(handlerCall, options.HandlerGraph, endpoints);
296297

297298
Handlers.Remove(handlerCall);
298299

src/Wolverine/Runtime/Handlers/HandlerGraph.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ IEnumerable<HandlerChain> explodeChains(HandlerChain chain)
319319

320320
// This lovely thing was brought to you by https://github.com/JasperFx/wolverine/issues/2004
321321
var duplicateTypeNames = allChains
322+
.Where(x => x.Handlers.Any()) // filter out the parent, placeholder HandlerChain for separated handlers
322323
.GroupBy(x => x.TypeName)
323324
.Where(x => x.Count() > 1)
324325
.ToArray();

0 commit comments

Comments
 (0)