Skip to content

Solution for the Instructor.Core nuget package. See the readme.

License

Notifications You must be signed in to change notification settings

code-dispenser/Instructor

Repository files navigation

.NET Coverage Status Nuget download

Instructor icon Instructor

Overview

Instructor is a lightweight, minimalist library focused on one job: dispatching instructions (commands and queries) to their respective handlers.

It integrates seamlessly with your existing IoC container—like Autofac or Microsoft.Extensions.DependencyInjection—without enforcing conventions or adding unnecessary abstractions. All handler resolution is delegated to the container you already use.

Example Usage

A working example can be found in the Instructor GitHub Repository.

Note: The None type (aka Unit) shown below is not within the Instructor.Core package, its within the demo project.

Define Commands and Queries (aka Instructions)

Just implement IInstruction<TValue> for each instruction.

using Instructor.Core.Common.Seeds;

public class AddCustomerCommand: IInstruction<None>
{
    public Guid   CustomerID   { get;}
    public string CustomerName { get; } = default!;

    public AddCustomerCommand(CustomerData customerData)

        => (CustomerID, CustomerName) = (customerData.CustomerID, customerData.CustomerName);
}

public class GetCustomerQuery(Guid customerID) : IInstruction<CustomerData>
{
    public Guid CustomerID { get; } = customerID;
}

Define Handlers

Implement IInstructionHandler<TInstruction, TValue> or optionally the marker interfaces ICommandHandler<TInstruction, TValue> or IQueryHandler<TInstruction, TValue>.

using Instructor.Core.Common.Seeds;

public class AddCustomerCommandHandler : ICommandHandler<AddCustomerCommand, None>
{
    public AddCustomerCommandHandler(){ }//TODO: inject database dependencies here

    public async Task<None> Handle(AddCustomerCommand instruction, CancellationToken cancellationToken)
    {
        await Console.Out.WriteLineAsync($"Added customer {instruction.CustomerName} with ID {instruction.CustomerID} to the database (not).");
        return None.Value;
    }
}

public class GetCustomerQueryHandler : IQueryHandler<GetCustomerQuery, CustomerData>
{
    public GetCustomerQueryHandler() { } //TODO: inject dependencies here as usual
    public async Task<CustomerData> Handle(GetCustomerQuery instruction, CancellationToken cancellationToken)
    {
        await Console.Out.WriteLineAsync($"Getting the customer with ID {instruction.CustomerID} from the database (not).");

        return new CustomerData(instruction.CustomerID, "John Doe");
    }
}

Register the InstructionDispatcher and its factory delegate that defers resolution of instruction handlers via the underlying IoC container

Autofcac example:

 using Instructor.Core;
 using Instructor.Core.Common.Seeds;

    /*
        * Scan assemblies or add handlers manually i.e. builder.RegisterType<AddCustomerCommandHandler>().As<IInstructionHandler<AddCustomerCommand, None>>()
    */
    builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies()).AsClosedTypesOf(typeof(IInstructionHandler<,>));
    builder.Register<InstructionDispatcher>(c =>
    {
        var context = c.Resolve<IComponentContext>();
        return new InstructionDispatcher(type => context.Resolve(type));
    
    }).As<IInstructionDispatcher>().InstancePerLifetimeScope();

Microsoft.Extensions.DependencyInjection example:

using Instructor.Core;
using Instructor.Core.Common.Seeds;

   /*
       * Add handlers manually or get a package like Scrutor to scan assemblies
   */
   .Services.AddTransient<IInstructionHandler<AddCustomerCommand, None>, AddCustomerCommandHandler>()
           .AddTransient<IInstructionHandler<GetCustomerQuery, CustomerData>, GetCustomerQueryHandler>()
           .AddSingleton<IInstructionDispatcher>(provider => new InstructionDispatcher(type => provider.GetRequiredService(type)))

Send your instructions via the InstructionDispatcher to be processed by the handlers

using Instructor.Core;
using Instructor.Core.Common.Seeds;

   private readonly IInstructionDispatcher _instructionDispatcher;//set by constructor injection

   var newCustomer = new CustomerData(Guid.NewGuid(), "John Doe");//Data recieved from some client

   _ = await __instructionDispatcher.SendInstruction(new AddCustomerCommand(newCustomer));//just dispatch the command (instruction) for handling

   var customerData = await __instructionDispatcher.SendInstruction(new GetCustomerQuery(newCustomer.CustomerID));

About

Solution for the Instructor.Core nuget package. See the readme.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages