cat on a spaceship

This commit is contained in:
2025-11-01 22:38:46 -04:00
parent 747fa227a1
commit f6dccf46d7
89 changed files with 2663 additions and 581 deletions
@@ -0,0 +1,14 @@
using System;
namespace Svrnty.CQRS.Abstractions.Attributes;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class CommandNameAttribute : Attribute
{
public CommandNameAttribute(string name)
{
Name = name;
}
public string Name { get; }
}
@@ -0,0 +1,15 @@
using System;
namespace Svrnty.CQRS.Abstractions.Attributes;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class QueryNameAttribute : Attribute
{
public QueryNameAttribute(string name)
{
Name = name;
}
public string Name { get; }
}
@@ -0,0 +1,51 @@
using System;
using System.Reflection;
using Svrnty.CQRS.Abstractions.Attributes;
namespace Svrnty.CQRS.Abstractions.Discovery;
public sealed class CommandMeta : ICommandMeta
{
public CommandMeta(Type commandType, Type serviceType, Type commandResultType)
{
CommandType = commandType;
ServiceType = serviceType;
CommandResultType = commandResultType;
}
public CommandMeta(Type commandType, Type serviceType)
{
CommandType = commandType;
ServiceType = serviceType;
}
private CommandNameAttribute NameAttribute => CommandType.GetCustomAttribute<CommandNameAttribute>();
public string Name
{
get
{
var name = NameAttribute?.Name ?? CommandType.Name.Replace("Command", string.Empty);
return name;
}
}
public Type CommandType { get; }
public Type ServiceType { get; }
public Type CommandResultType { get; }
public string LowerCamelCaseName
{
get
{
if (string.IsNullOrEmpty(Name))
return Name;
var name = Name;
var firstLetter = Char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name.Substring(1)}";
return ret;
}
}
}
@@ -0,0 +1,13 @@
using System;
namespace Svrnty.CQRS.Abstractions.Discovery;
public interface ICommandMeta
{
string Name { get; }
Type CommandType { get; }
Type ServiceType { get; }
Type CommandResultType { get; }
string LowerCamelCaseName { get; }
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
namespace Svrnty.CQRS.Abstractions.Discovery;
public interface IQueryDiscovery
{
IQueryMeta FindQuery(string name);
IQueryMeta FindQuery(Type queryType);
IEnumerable<IQueryMeta> GetQueries();
bool QueryExists(string name);
bool QueryExists(Type queryType);
}
public interface ICommandDiscovery
{
bool CommandExists(string name);
bool CommandExists(Type commandType);
ICommandMeta FindCommand(string name);
ICommandMeta FindCommand(Type commandType);
IEnumerable<ICommandMeta> GetCommands();
}
@@ -0,0 +1,13 @@
using System;
namespace Svrnty.CQRS.Abstractions.Discovery;
public interface IQueryMeta
{
string Name { get; }
Type QueryType { get; }
Type ServiceType { get; }
Type QueryResultType { get; }
string Category { get; }
string LowerCamelCaseName { get; }
}
@@ -0,0 +1,46 @@
using System;
using System.Reflection;
using Svrnty.CQRS.Abstractions.Attributes;
namespace Svrnty.CQRS.Abstractions.Discovery;
public class QueryMeta : IQueryMeta
{
public QueryMeta(Type queryType, Type serviceType, Type queryResultType)
{
QueryType = queryType;
ServiceType = serviceType;
QueryResultType = queryResultType;
}
protected virtual QueryNameAttribute NameAttribute => QueryType.GetCustomAttribute<QueryNameAttribute>();
public virtual string Name
{
get
{
var name = NameAttribute?.Name ?? QueryType.Name.Replace("Query", string.Empty);
return name;
}
}
public virtual Type QueryType { get; }
public virtual Type ServiceType { get; }
public virtual Type QueryResultType { get; }
public virtual string Category => "BasicQuery";
public string LowerCamelCaseName
{
get
{
if (string.IsNullOrEmpty(Name))
return Name;
var name = Name;
var firstLetter = char.ToLowerInvariant(name[0]);
var ret = $"{firstLetter}{name[1..]}";
return ret;
}
}
}
@@ -0,0 +1,16 @@
using System.Threading;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Abstractions;
public interface ICommandHandler<in TCommand>
where TCommand : class
{
Task HandleAsync(TCommand command, CancellationToken cancellationToken = default);
}
public interface ICommandHandler<in TCommand, TCommandResult>
where TCommand : class
{
Task<TCommandResult> HandleAsync(TCommand command, CancellationToken cancellationToken = default);
}
+10
View File
@@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Abstractions;
public interface IQueryHandler<in TQuery, TQueryResult>
where TQuery : class
{
Task<TQueryResult> HandleAsync(TQuery query, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,8 @@
namespace Svrnty.CQRS.Abstractions.Security;
public enum AuthorizationResult
{
Unauthorized,
Forbidden,
Allowed
}
@@ -0,0 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Abstractions.Security;
public interface ICommandAuthorizationService
{
Task<AuthorizationResult> IsAllowedAsync(Type commandType, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Svrnty.CQRS.Abstractions.Security;
public interface IQueryAuthorizationService
{
Task<AuthorizationResult> IsAllowedAsync(Type queryType, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,50 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Abstractions.Discovery;
namespace Svrnty.CQRS.Abstractions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddQuery<TQuery, TQueryResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TQueryHandler>(this IServiceCollection services)
where TQuery : class
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
{
// add handler to DI.
services.AddTransient<IQueryHandler<TQuery, TQueryResult>, TQueryHandler>();
// add for discovery purposes.
var queryMeta = new QueryMeta(typeof(TQuery), typeof(IQueryHandler<TQuery, TQueryResult>), typeof(TQueryResult));
services.AddSingleton<IQueryMeta>(queryMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, TCommandResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand, TCommandResult>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>), typeof(TCommandResult));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
}
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsAotCompatible>true</IsAotCompatible>
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
<LangVersion>14</LangVersion>
<Company>Svrnty</Company>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/svrnty/dotnet-cqrs</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
<IncludeSymbols>true</IncludeSymbols>
<IncludeSource>true</IncludeSource>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<ItemGroup>
<None Include="..\icon.png" Pack="true" PackagePath="" CopyToOutputDirectory="Always" />
<None Include="..\README.md" Pack="true" PackagePath="" CopyToOutputDirectory="Always" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.2.25502.107" />
</ItemGroup>
</Project>