Last active
August 30, 2021 22:40
-
-
Save djeikyb/bacd11762366561cd3a951d1c8d91783 to your computer and use it in GitHub Desktop.
Demonstrates how a single consumer could be used to apply domain logic to many different models that can be described by a common interface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading.Tasks; | |
using MassTransit; | |
using MassTransit.Mediator; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using Serilog; | |
using Serilog.Sinks.SystemConsole.Themes; | |
namespace Trolley | |
{ | |
/// <summary> | |
/// Can encapsulate business logic for a process that applies to many types | |
/// of IFood. Implementation specific behaviours can be accessed via a type | |
/// specific gateway. | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
class MyConsumer<T> : IConsumer<MyCommand<T>> | |
where T : IFood | |
{ | |
private readonly IGateway<T> _gateway; | |
public MyConsumer(IGateway<T> gateway) | |
{ | |
_gateway = gateway; | |
} | |
public Task Consume(ConsumeContext<MyCommand<T>> context) | |
{ | |
MyCommand<T>? cmd = context.Message; | |
T food = _gateway.OpenCambro(); | |
// Helps visually match which command was handled by which consumer. | |
// Does not affect the mechanics of this pubsub dispatch pattern. | |
// And it's nice to have at least one property on the command, like | |
// a real command would likely have. | |
var foodEnum = cmd.Food; | |
Log.Information($"{GetType()} consumer invoked with {foodEnum}. {food.Moji}"); | |
return Task.CompletedTask; | |
} | |
} | |
class SpaghattaNadleGateway : IGateway<SpaghattaNadle> | |
{ | |
public SpaghattaNadle OpenCambro() | |
{ | |
return new SpaghattaNadle(); | |
} | |
} | |
class AvacadaGateway : IGateway<Avacada> | |
{ | |
public Avacada OpenCambro() | |
{ | |
return new Avacada(); | |
} | |
} | |
/// <typeparam name="T"> | |
/// Used by MassTransit for dispatch. T here must match T in the consumer. | |
/// </typeparam> | |
// ReSharper disable once UnusedTypeParameter | |
class MyCommand<T> | |
where T : IFood | |
{ | |
public Food Food { get; init; } | |
} | |
interface IFood | |
{ | |
public Food FoodKind { get; } | |
public string Moji { get; } | |
/// <summary> | |
/// While convenient for this demo, I expect any non-trivial application | |
/// of this template will need a more complex command factory. I don't | |
/// expect such a factory to live in this interface. | |
/// </summary> | |
public static MyCommand<T> Command<T>() where T : IFood, new() | |
{ | |
return new MyCommand<T> { Food = new T().FoodKind }; | |
} | |
} | |
class Program | |
{ | |
static async Task Main() | |
{ | |
Log.Logger = new LoggerConfiguration() | |
.Enrich.FromLogContext() | |
.WriteTo.Console(theme: ConsoleTheme.None) | |
.CreateLogger(); | |
var host = Host.CreateDefaultBuilder() | |
.UseSerilog() | |
.ConfigureServices((_, services) => | |
{ | |
services.AddSingleton<IGateway<Avacada>, AvacadaGateway>(); | |
services.AddSingleton<IGateway<SpaghattaNadle>, SpaghattaNadleGateway>(); | |
services.AddMediator(cfg => | |
{ | |
cfg.AddConsumer<MyConsumer<Avacada>>(); | |
cfg.AddConsumer<MyConsumer<SpaghattaNadle>>(); | |
}); | |
}) | |
.Build(); | |
Log.Information("Hello World!"); | |
var mediator = host.Services.GetRequiredService<IMediator>(); | |
await mediator.Publish(IFood.Command<Avacada>()); | |
await mediator.Publish(IFood.Command<SpaghattaNadle>()); | |
} | |
} | |
class Avacada : IFood | |
{ | |
public Food FoodKind => Food.Avacada; | |
public string Moji => "🥑"; | |
} | |
class SpaghattaNadle : IFood | |
{ | |
public Food FoodKind => Food.SpaghattaNadle; | |
public string Moji => "🐍"; | |
} | |
enum Food | |
{ | |
Avacada, | |
SpaghattaNadle, | |
} | |
interface IGateway<out T> | |
where T : IFood | |
{ | |
T OpenCambro(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>net5.0</TargetFramework> | |
<Nullable>enable</Nullable> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="MassTransit" Version="7.2.2"/> | |
<PackageReference Include="MassTransit.Extensions.DependencyInjection" Version="7.2.2"/> | |
<PackageReference Include="MassTransit.AspNetCore" Version="7.2.2"/> | |
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0"/> | |
<PackageReference Include="Serilog" Version="2.10.0"/> | |
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.1.2"/> | |
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0"/> | |
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0"/> | |
</ItemGroup> | |
</Project> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://martinfowler.com/articles/gateway-pattern.html