-
Notifications
You must be signed in to change notification settings - Fork 2
Refactor to allow single consumer with multiple handlers #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
77e0cb8
d45e85c
0aa2201
92ddffc
67fb8b2
e1baf1c
1719445
7b6b777
273edf2
309cc73
8274516
a35c9a8
f498f56
8519991
6034d79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,24 @@ | ||
| <!-- For more info on central package management go to https://devblogs.microsoft.com/nuget/introducing-central-package-management/ --> | ||
| <Project> | ||
| <PropertyGroup> | ||
| <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PackageVersion Include="AwesomeAssertions" Version="8.1.0" /> | ||
| <PackageVersion Include="coverlet.collector" Version="6.0.4" /> | ||
| <PackageVersion Include="FluentAssertions" Version="7.0.0" /> | ||
| <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" /> | ||
| <PackageVersion Include="NSubstitute" Version="5.3.0" /> | ||
| <PackageVersion Include="xunit" Version="2.9.3" /> | ||
| <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" /> | ||
| <PackageVersion Include="Confluent.Kafka" Version="2.10.0" /> | ||
| <PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" /> | ||
| <PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.10.0" /> | ||
| <PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" /> | ||
| <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" /> | ||
| <PackageVersion Include="SonarAnalyzer.CSharp" Version="10.8.0.113526" /> | ||
| <PackageVersion Include="System.Linq.Async" Version="6.0.1" /> | ||
| <PackageVersion Include="Microsoft.Sbom.Targets" Version="3.0.0" /> | ||
| <PackageVersion Include="RocksDB" Version="9.10.0.55496" /> | ||
| </ItemGroup> | ||
| <PropertyGroup> | ||
| <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PackageVersion Include="AwesomeAssertions" Version="9.1.0" /> | ||
| <PackageVersion Include="coverlet.collector" Version="6.0.4" /> | ||
| <PackageVersion Include="FluentAssertions" Version="7.0.0" /> | ||
| <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> | ||
| <PackageVersion Include="NSubstitute" Version="5.3.0" /> | ||
| <PackageVersion Include="xunit" Version="2.9.3" /> | ||
| <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.1" /> | ||
| <PackageVersion Include="Confluent.Kafka" Version="2.11.0" /> | ||
| <PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" /> | ||
| <PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.10.0" /> | ||
| <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" /> | ||
| <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" /> | ||
| <PackageVersion Include="SonarAnalyzer.CSharp" Version="10.13.0.120203" /> | ||
| <PackageVersion Include="System.Linq.Async" Version="6.0.3" /> | ||
| <PackageVersion Include="Microsoft.Sbom.Targets" Version="4.0.3" /> | ||
| <PackageVersion Include="RocksDB" Version="10.2.1.58549" /> | ||
| </ItemGroup> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| using MinimalKafka.Aggregates; | ||
|
|
||
| namespace Examples.Aggregate; | ||
|
|
||
| /// <summary> | ||
| /// Example aggregate for testing purposes. Supports increment, decrement, and set operations on a counter. | ||
| /// </summary> | ||
| public record Test : IAggregate<Guid, Test, TestCommands> | ||
| { | ||
| /// <summary> | ||
| /// Gets the aggregate identifier. | ||
| /// </summary> | ||
| public Guid Id { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the aggregate version. | ||
| /// </summary> | ||
| public int Version { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the current counter value. | ||
| /// </summary> | ||
| public int Counter { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Creates a new instance of <see cref="Test"/> aggregate from a command. | ||
| /// </summary> | ||
| /// <param name="command">The command to initialize the aggregate.</param> | ||
| /// <returns>A new <see cref="Test"/> aggregate wrapped in a <see cref="Result{Test}"/>.</returns> | ||
| public static Result<Test> Create(TestCommands command) | ||
| => new Test() { Id = command.Id, Version = 0 }; | ||
|
|
||
| /// <summary> | ||
| /// Applies a command to the current state and returns the resulting state. | ||
| /// </summary> | ||
| /// <param name="state">The current aggregate state.</param> | ||
| /// <param name="command">The command to apply.</param> | ||
| /// <returns>The new state as a <see cref="Result{Test}"/>.</returns> | ||
| public static Result<Test> Apply(Test state, TestCommands command) | ||
| { | ||
| var result = command.CommandName switch | ||
| { | ||
| nameof(Create) => Create(command), | ||
| nameof(Increment) => state.Increment(), | ||
| nameof(Decrement) => state.Decrement(), | ||
| nameof(SetCounter) => state.SetCounter(command.SetCounter!), | ||
| _ => Result.Failed(state, "Unknown command: " + command.CommandName) | ||
| }; | ||
|
|
||
| if (result.IsSuccess) | ||
| { | ||
| return result.State with { Version = state.Version + 1 }; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Increments the counter by one. | ||
| /// </summary> | ||
| /// <returns>New state if successful, or failed result if out of bounds.</returns> | ||
| public Result<Test> Increment() | ||
| { | ||
| if (Counter >= 100) | ||
| { | ||
| return Result.Failed(this, "Counter cannot exceed 100."); | ||
| } | ||
|
|
||
| return this with { Counter = Counter + 1 }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Decrements the counter by one. | ||
| /// </summary> | ||
| /// <returns>New state if successful, or failed result if out of bounds.</returns> | ||
| public Result<Test> Decrement() | ||
| { | ||
| if (Counter <= 0) | ||
| { | ||
| return Result.Failed(this, "Counter cannot be less than 0."); | ||
| } | ||
|
|
||
| return this with { Counter = Counter - 1 }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets the counter to a specific value using the <see cref="SetCounter"/> command. | ||
| /// </summary> | ||
| /// <param name="cmd">The command containing the new counter value.</param> | ||
| /// <returns>New state if within bounds, or failed result otherwise.</returns> | ||
| public Result<Test> SetCounter(SetCounter cmd) | ||
| { | ||
| if (cmd.Counter < 0) | ||
| { | ||
| return Result.Failed(this, "Counter cannot be less than 0."); | ||
| } | ||
|
|
||
| if (cmd.Counter > 100) | ||
| { | ||
| return Result.Failed(this, "Counter cannot be more than 100."); | ||
| } | ||
|
|
||
| return this with { Counter = cmd.Counter }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| using MinimalKafka.Aggregates; | ||
|
|
||
| namespace Examples.Aggregate; | ||
|
|
||
| public class TestCommands : IAggregateCommands<Guid> | ||
| { | ||
| public Guid Id { get; init; } = Guid.NewGuid(); | ||
| public required int Version { get; init; } | ||
| public required string CommandName { get; init; } | ||
| public SetCounter? SetCounter { get; set; } | ||
|
|
||
| } | ||
|
|
||
| public record SetCounter(int Counter); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| using Confluent.Kafka; | ||
| using Examples; | ||
| using Examples.Aggregate; | ||
| using MinimalKafka; | ||
| using MinimalKafka.Extension; | ||
| using MinimalKafka.Serializers; | ||
| using MinimalKafka.Aggregates; | ||
| using MinimalKafka.Stream; | ||
|
|
||
| var builder = WebApplication.CreateBuilder(args); | ||
|
|
@@ -11,66 +11,83 @@ | |
| { | ||
| config | ||
| .WithConfiguration(builder.Configuration.GetSection("Kafka")) | ||
| .WithBootstrapServers("nas:9092") | ||
| .WithGroupId(AppDomain.CurrentDomain.FriendlyName) | ||
| .WithClientId(AppDomain.CurrentDomain.FriendlyName) | ||
| .WithTransactionalId(AppDomain.CurrentDomain.FriendlyName) | ||
|
Comment on lines
+15
to
+17
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Using AppDomain.CurrentDomain.FriendlyName for multiple Kafka settings may cause issues. Using the same value for - .WithGroupId(AppDomain.CurrentDomain.FriendlyName)
- .WithClientId(AppDomain.CurrentDomain.FriendlyName)
- .WithTransactionalId(AppDomain.CurrentDomain.FriendlyName)
+ .WithGroupId($"{AppDomain.CurrentDomain.FriendlyName}-group")
+ .WithClientId($"{AppDomain.CurrentDomain.FriendlyName}-client")
+ .WithTransactionalId($"{AppDomain.CurrentDomain.FriendlyName}-tx")🤖 Prompt for AI Agents |
||
| .WithOffsetReset(AutoOffsetReset.Earliest) | ||
| .WithPartitionAssignedHandler((_, p) => p.Select(tp => new TopicPartitionOffset(tp, Offset.Beginning))) | ||
| .WithJsonSerializers() | ||
| .UseRocksDB(); | ||
| .UseRocksDB(x => | ||
| { | ||
| x.DataPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RocksDB"); | ||
| }); | ||
|
|
||
| }); | ||
|
|
||
| var app = builder.Build(); | ||
|
|
||
| app.MapTopic("my-topic", ([FromKey] string key, [FromValue] string value) => | ||
| { | ||
| Console.WriteLine($"Received: {key} - {value}"); | ||
|
|
||
| Console.WriteLine("##################"); | ||
| Console.WriteLine("my-topic"); | ||
| Console.WriteLine("##################"); | ||
| }); | ||
| app.MapAggregate<Test, Guid, TestCommands>("tests"); | ||
|
|
||
| app.MapStream<Guid, LeftObject>("left") | ||
| .Join<int, RightObject>("right").On((l, r) => l.RightObjectId == r.Id) | ||
| .Into((c, v) => | ||
| { | ||
| var (left, right) = v; | ||
|
|
||
| Console.WriteLine("##################"); | ||
| Console.WriteLine("LEFT Join Right"); | ||
| Console.WriteLine("##################"); | ||
| //app.MapTopic("my-topic", ([FromKey] string key, [FromValue] string value) => | ||
| //{ | ||
| // Console.WriteLine($"Received: {key} - {value}"); | ||
|
|
||
| return Task.CompletedTask; | ||
| }).WithGroupId("group1"); | ||
| // Console.WriteLine("##################"); | ||
| // Console.WriteLine("my-topic"); | ||
| // Console.WriteLine("##################"); | ||
| //}); | ||
|
|
||
| app.MapStream<Guid, LeftObject>("left") | ||
| .Into(async (c, k, v) => | ||
| { | ||
| v = v with { RightObjectId = 2 }; | ||
|
|
||
| Console.WriteLine("##################"); | ||
| Console.WriteLine("LEFT INTO UPDATE"); | ||
| Console.WriteLine("##################"); | ||
| //app.MapTopic("my-topic", ([FromKey] string key, [FromValue] string value) => | ||
| //{ | ||
| // Console.WriteLine($"Received: {key} - {value}"); | ||
|
|
||
| await c.ProduceAsync("left-update", k, v); | ||
| }).WithGroupId("group2"); | ||
| // Console.WriteLine("##################"); | ||
| // Console.WriteLine("my-topic"); | ||
| // Console.WriteLine("##################"); | ||
| //}); | ||
|
|
||
| //app.MapStream<Guid, LeftObject>("left") | ||
| // .Join<int, RightObject>("right").On((l, r) => l.RightObjectId == r.Id) | ||
| // .Into((c, v) => | ||
| // { | ||
| // var (left, right) = v; | ||
|
|
||
| app.MapStream<int, RightObject>("right") | ||
| .Join<Guid, LeftObject>("left").On((k, v) => k, (k, v) => v.RightObjectId) | ||
| .Into((c, k, v) => | ||
| { | ||
| var (left, right) = v; | ||
| // Console.WriteLine("##################"); | ||
| // Console.WriteLine("LEFT Join Right"); | ||
| // Console.WriteLine("##################"); | ||
|
|
||
| Console.WriteLine("##################"); | ||
| Console.WriteLine("RIGHT JOIN LEFT"); | ||
| Console.WriteLine("##################"); | ||
| // return Task.CompletedTask; | ||
| // }); | ||
|
|
||
| return Task.CompletedTask; | ||
| }) | ||
| .WithGroupId("group3"); | ||
| //app.MapStream<Guid, LeftObject>("left") | ||
| // .Into(async (c, k, v) => | ||
| // { | ||
| // v = v with { RightObjectId = 2 }; | ||
|
|
||
| // Console.WriteLine("##################"); | ||
| // Console.WriteLine("LEFT INTO UPDATE"); | ||
| // Console.WriteLine("##################"); | ||
|
|
||
| // await c.ProduceAsync("left-update", k, v); | ||
| // }); | ||
|
|
||
|
|
||
| //app.MapStream<int, RightObject>("right") | ||
| // .Join<Guid, LeftObject>("left").On((k, v) => k, (k, v) => v.RightObjectId) | ||
| // .Into((c, k, v) => | ||
| // { | ||
| // var (left, right) = v; | ||
|
|
||
| // Console.WriteLine("##################"); | ||
| // Console.WriteLine("RIGHT JOIN LEFT"); | ||
| // Console.WriteLine("##################"); | ||
|
|
||
| // return Task.CompletedTask; | ||
| // }); | ||
|
|
||
|
|
||
| await app.RunAsync(); | ||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.