Skip to content
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

Add EventHub #846

Draft
wants to merge 32 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6527036
Cleanup
gritcsenko May 27, 2024
5ef065b
Add ScopeAccessorExtensions
gritcsenko May 27, 2024
199d351
Remove unsued ICreationAuditableEntity
gritcsenko May 27, 2024
3447c16
Remove need to speciy TraceIdentifier
gritcsenko May 27, 2024
20c8726
Cleanup
gritcsenko May 27, 2024
13cda34
Refactor EventsCollection
gritcsenko May 27, 2024
9c31899
Add simple EventHub
gritcsenko May 27, 2024
de83b2e
Simplify events
gritcsenko May 27, 2024
70019d4
Refactor and simplify stuff
gritcsenko May 28, 2024
bfa0ec4
Remove MediatR
gritcsenko Jun 3, 2024
c9e481b
Merge branch 'main' into feature/845-rx
gritcsenko Jun 3, 2024
a9e5f97
Fix Build
gritcsenko Jun 3, 2024
c8e8f11
Fix Formatting
gritcsenko Jun 3, 2024
b004bc7
Fix tests
gritcsenko Jun 3, 2024
363de93
Fix tests
gritcsenko Jun 8, 2024
16ea7c6
Merge branch 'main' into feature/845-rx
gritcsenko Jun 8, 2024
4e873b2
Fix integration tests
gritcsenko Jun 8, 2024
c98620f
Remove unused AssemblyReference
gritcsenko Jun 25, 2024
17c605d
Merge branch 'main' into feature/845-rx
gritcsenko Jun 25, 2024
6ea95f4
Merge branch 'main' into feature/845-rx
gritcsenko Jun 26, 2024
da73be2
Merge branch 'main' into feature/845-rx
gritcsenko Jul 3, 2024
7499181
Merge branch 'main' into feature/845-rx
gritcsenko Jul 21, 2024
f9a4670
Merge branch 'main' into feature/845-rx
gritcsenko Jul 22, 2024
d0b53f7
Merge branch 'main' into feature/845-rx
gritcsenko Aug 11, 2024
fc9138f
Merge branch 'main' into feature/845-rx
gritcsenko Aug 13, 2024
25f72b3
Fix formatting
gritcsenko Aug 13, 2024
72b2b65
Fix Tests
gritcsenko Aug 13, 2024
461bf0e
Merge branch 'main' into feature/845-rx
gritcsenko Aug 15, 2024
99cf1c2
Fix tests
gritcsenko Aug 18, 2024
f2882a8
Merge branch 'main' into feature/845-rx
gritcsenko Aug 18, 2024
389c488
Merge branch 'main' into feature/845-rx
gritcsenko Aug 19, 2024
0dd2de9
Merge branch 'main' into feature/845-rx
gritcsenko Aug 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/HomeInventory/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@
<Using Include="HomeInventory.Core" Condition="$(MSBuildProjectName.Contains('Contracts')) != true"/>
</ItemGroup>

<ItemGroup Condition="$(MSBuildProjectName.Contains('Contracts')) != true">
<Using Include="LanguageExt" />
<Using Include="LanguageExt.Common" />
<Using Include="HomeInventory.Core" />
</ItemGroup>

</Project>
2 changes: 0 additions & 2 deletions src/HomeInventory/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
<PackageVersion Include="LanguageExt.SysX" Version="4.4.8" />
<PackageVersion Include="LanguageExt.Transformers" Version="4.4.8" />
<PackageVersion Include="Mediator.Abstractions" Version="2.1.7" />
<PackageVersion Include="MediatR" Version="12.4.0" />
<PackageVersion Include="MediatR.Contracts" Version="2.0.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
<PackageVersion Include="Microsoft.FeatureManagement" Version="3.5.0" />
<PackageVersion Include="Microsoft.FeatureManagement.AspNetCore" Version="3.5.0" />
Expand Down
2 changes: 1 addition & 1 deletion src/HomeInventory/HomeInventory.Api/AppBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public WebApplication Build()

private static IServiceCollection AddServices(IServiceCollection services) =>
services
.AddMediatR(
.AddMessageHub(
Application.AssemblyReference.Assembly,
Application.UserManagement.AssemblyReference.Assembly)
.AddDomain()
Expand Down
31 changes: 0 additions & 31 deletions src/HomeInventory/HomeInventory.Api/MediatRConfigurator.cs

This file was deleted.

16 changes: 16 additions & 0 deletions src/HomeInventory/HomeInventory.Api/MesssageHubConfigurator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Reflection;

namespace Microsoft.Extensions.DependencyInjection;

internal static class MesssageHubConfigurator
{
public static IServiceCollection AddMessageHub(this IServiceCollection services, params Assembly[] serviceAssemblies)
{
services.AddMessageHubCore();
foreach (var serviceAssembly in serviceAssemblies)
{
services.AddMessageHubServicesFrom(serviceAssembly);
}
return services;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Reflection;
using HomeInventory.Application;
using HomeInventory.Application.Framework.Mapping;
using HomeInventory.Domain.Primitives.Messages;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection;

Expand All @@ -14,4 +16,52 @@ public static IServiceCollection AddMappingAssemblySource(this IServiceCollectio
public static IServiceCollection AddMappingTypeConverter(this IServiceCollection services) =>
services
.AddSingleton(typeof(TypeConverterAdapter<,,>));

public static IServiceCollection AddMessageHubCore(this IServiceCollection services)
{
services.AddSingleton<IMessageObservableProvider, MessageObservableProvider>();
services.AddSingleton<IMessageHubContext, MessageHubContext>();
services.AddSingleton<IMessageHub, MessageHub>();
services.AddSingleton(typeof(RequestHandlerAdapter<,>));
services.AddSingleton(typeof(MessageHandlerAdapter<>));
return services;
}

public static IServiceCollection AddMessageHubServicesFrom(this IServiceCollection services, Assembly assembly)
{
foreach (var type in assembly.DefinedTypes.Where(t => !t.IsOpenGeneric() && t.CanBeInstantiated()))
{
services.Add(type.GetServicesOfType(typeof(IMessageHandler<>)));
services.Add(type.GetServicesOfType(typeof(IRequestHandler<,>)));
}

foreach (var type in assembly.DefinedTypes.Where(t => t.IsOpenGeneric() && t.CanBeInstantiated()))
{
services.Add(type.GetServicesOfType2(typeof(IMessagePipelineBehavior<>)));
services.Add(type.GetServicesOfType2(typeof(IRequestPipelineBehavior<,>)));
}

return services;
}

private static IEnumerable<ServiceDescriptor> GetServicesOfType(this Type type, Type serviceTemplateType)
{
foreach (var iface in type.GetInterfaces().Where(i => i.IsGenericType).Where(i => i.GetGenericTypeDefinition() == serviceTemplateType))
{
yield return new ServiceDescriptor(iface, type, ServiceLifetime.Singleton);
}
}

private static IEnumerable<ServiceDescriptor> GetServicesOfType2(this Type type, Type serviceTemplateType)
{
foreach (var iface in type.GetInterfaces().Where(i => i.IsGenericType).Where(i => i.GetGenericTypeDefinition() == serviceTemplateType))
{
yield return new ServiceDescriptor(type, type, ServiceLifetime.Singleton);
yield return new ServiceDescriptor(serviceTemplateType, type, ServiceLifetime.Singleton);
}
}

private static bool IsOpenGeneric(this Type type) => type.IsGenericTypeDefinition || type.ContainsGenericParameters;

private static bool CanBeInstantiated(this Type type) => !(type.IsAbstract || type.IsInterface);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
using HomeInventory.Domain.Primitives;
using HomeInventory.Domain.Primitives.Messages;

namespace HomeInventory.Application.Cqrs.DomainEvents;

public static class DomainEventNotification
{
public static INotification Create(IDomainEvent domainEvent) =>
(INotification)Activator.CreateInstance(typeof(DomainEventNotification<>).MakeGenericType(domainEvent.GetType()), domainEvent)!;
public static IMessage CreateDomainNotification(this IDomainEvent domainEvent) =>
(IMessage)Activator.CreateInstance(typeof(DomainEventNotification<>).MakeGenericType(domainEvent.GetType()), domainEvent)!;
}

public class DomainEventNotification<TEvent>(TEvent domainEvent) : INotification
public class DomainEventNotification<TEvent>(TEvent domainEvent) : IMessage
where TEvent : IDomainEvent
{
public TEvent DomainEvent { get; } = domainEvent;
public Ulid Id => DomainEvent.Id;
public DateTimeOffset CreatedOn => DomainEvent.CreatedOn;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="AutoMapper" />
<PackageReference Include="MediatR" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.FeatureManagement" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
global using MediatR;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using AutoMapper;
using System.Linq.Expressions;
using HomeInventory.Application.Mapping;
using HomeInventory.Domain.Primitives;
using HomeInventory.Domain.Primitives.Messages;

namespace HomeInventory.Application.Framework.Mapping;

Expand All @@ -10,56 +11,79 @@ protected BaseMappingsProfile()
{
}

protected IObjectMap<TSource> CreateMap<TSource>() => new ObjectMap<TSource>(this);
protected MapBuilder<TSource> CreateMap<TSource>() => new(this);
}

protected interface IObjectMap<TSource>
{
IMappingExpression<TSource, TDestination> To<TDestination>();
public abstract class BaseMapBuilder<TSource>(Profile profile)
{
protected Profile Profile { get; } = profile;

IMappingExpression<TSource, TDestination> Using<TDestination>(Func<TSource, ResolutionContext, TDestination> convertTo);
public IMappingExpression<TSource, TDestination> To<TDestination>() =>
Profile.CreateMap<TSource, TDestination>();

public IMappingExpression<TDestination, TSource> From<TDestination>() =>
Profile.CreateMap<TDestination, TSource>();
}

void Using<TDestination>(Expression<Func<TSource, TDestination>> convertTo, Expression<Func<TDestination, TSource>> convertFrom);
public sealed class MapBuilder<TSource>(Profile profile) : BaseMapBuilder<TSource>(profile)
{
public MapBuilder<TSource, TDependency> With<TDependency>() => new(Profile);

void Using<TDestination>(Expression<Func<TSource, TDestination>> convertTo, ObjectConverter<TDestination, TSource> converterFrom);
public IMappingExpression<TSource, TDestination> Using<TDestination>(Func<TSource, ResolutionContext, TDestination> convertTo) =>
To<TDestination>().ConstructUsing(convertTo);

void Using<TDestination, TFromValueConverter>(Expression<Func<TSource, TDestination>> convertTo)
where TFromValueConverter : ObjectConverter<TDestination, TSource>;
public void Using<TDestination>(Expression<Func<TSource, TDestination>> convertTo, Expression<Func<TDestination, TSource>> convertFrom)
{
To<TDestination>().ConvertUsing(convertTo);
From<TDestination>().ConvertUsing(convertFrom);
}

private sealed class ObjectMap<TSource>(Profile profile) : IObjectMap<TSource>
public void Using<TDestination>(Expression<Func<TSource, TDestination>> convertTo, ObjectConverter<TDestination, TSource> converterFrom)
{
private readonly Profile _profile = profile;

public IMappingExpression<TSource, TDestination> To<TDestination>() =>
_profile.CreateMap<TSource, TDestination>();
To<TDestination>().ConvertUsing(convertTo);
From<TDestination>().ConvertUsing(new TypeConverterAdapter<TDestination, TSource, ObjectConverter<TDestination, TSource>>(converterFrom));
}

public IMappingExpression<TSource, TDestination> Using<TDestination>(Func<TSource, ResolutionContext, TDestination> convertTo) =>
To<TDestination>()
.ConstructUsing(convertTo);
public void Using<TDestination, TFromValueConverter>(Expression<Func<TSource, TDestination>> convertTo)
where TFromValueConverter : ObjectConverter<TDestination, TSource>
{
To<TDestination>().ConvertUsing(convertTo);
From<TDestination>().ConvertUsing<TypeConverterAdapter<TDestination, TSource, TFromValueConverter>>();
}
}

public void Using<TDestination>(Expression<Func<TSource, TDestination>> convertTo, Expression<Func<TDestination, TSource>> convertFrom)
public sealed class MapBuilder<TSource, TDependency>(Profile profile) : BaseMapBuilder<TSource>(profile)
{
public MapBuilder<TSource, TDependency> Using<TDestination>(Func<TSource, TDependency, IRuntimeMapper, TDestination> convertTo)
{
To<TDestination>().ConstructUsing((src, ctx) =>
{
_profile.CreateMap<TSource, TDestination>()
.ConvertUsing(convertTo);
_profile.CreateMap<TDestination, TSource>()
.ConvertUsing(convertFrom);
}
var state = ctx.State ?? throw new InvalidOperationException("No dependency specificed");
if (state is not TDependency dependency)
{
throw new InvalidOperationException($"Cannot convert state {state} of type {state.GetType()} to {typeof(TDependency)}");
}

public void Using<TDestination>(Expression<Func<TSource, TDestination>> convertTo, ObjectConverter<TDestination, TSource> converterFrom)
{
_profile.CreateMap<TSource, TDestination>()
.ConvertUsing(convertTo);
_profile.CreateMap<TDestination, TSource>()
.ConvertUsing(new TypeConverterAdapter<TDestination, TSource, ObjectConverter<TDestination, TSource>>(converterFrom));
}

public void Using<TDestination, TFromValueConverter>(Expression<Func<TSource, TDestination>> convertTo)
where TFromValueConverter : ObjectConverter<TDestination, TSource>
return convertTo(src, dependency, ctx.Mapper);
});

return this;
}
public MapBuilder<TSource, TDependency> Using<TDestination, TResult>(Func<TSource, TDependency, IRuntimeMapper, TDestination> convertTo)
where TDestination : IRequestMessage<IQueryResult<TResult>>
where TResult : notnull
{
To<TDestination>().ConstructUsing((src, ctx) =>
{
_profile.CreateMap<TSource, TDestination>()
.ConvertUsing(convertTo);
_profile.CreateMap<TDestination, TSource>()
.ConvertUsing<TypeConverterAdapter<TDestination, TSource, TFromValueConverter>>();
}
var state = ctx.State ?? throw new InvalidOperationException("No dependency specificed");
if (state is not TDependency dependency)
{
throw new InvalidOperationException($"Cannot convert state {state} of type {state.GetType()} to {typeof(TDependency)}");
}

return convertTo(src, dependency, ctx.Mapper);
});

return this;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using AutoMapper;
using HomeInventory.Application.Mapping;
using HomeInventory.Domain.Primitives;

namespace HomeInventory.Application.Framework.Mapping;

Expand Down
Loading
Loading