Skip to content

madebykrol/InteractR

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

InteractR

Build Status

Inspired by the ideas from "clean architecture" and MediatR.

InteractR is used as a way to create a clean separation between the client and the domain / business logic.

Install from nuget.

PM > Install-Package InteractR -Version 8.0.0

Howto: Interactor

Usecase

class GreetUseCase : IUseCase<IGreetUseCaseOutputPort> {
	public string Name {get;}
	public GreetUseCase(name) {
		Guard.AgainstNullOrEmpty(name, nameof(name)); // Throw is name is null or empty
			
		Name = name;
	}
}

Interactor

class GreetUseCaseInteractor : IInteractor<GreetUseCase, IGreetUseCaseOutputPort> 
{
	public Task<UseCaseResult> Execute(GreetUseCase useCase, IGreetUseCaseOutputPort outputPort, CancellationToken cancellationToken)
	{
		outputPort.DisplayGreeting($"Hello, {useCase.Name}");
		
		return Task.FromResult(new UseCaseResult(true));
	}
}

Usage Console App

public class ConsoleOutput : IGreetUseCaseOutputPort {
	public void DisplayGreeting(string message) {
		Console.WriteLine(message);
	}
}
// Registration
var resolver = new SelfContainedResolver();
resolver.Register(new GreetUseCaseInteractor());

var interactorHub = new Hub(_resolver);

var console = new ConsoleOutput();

await interactorHub.Execute(new GreetUseCase("John Doe"), console);
// Would display Hello, John Doe in a console application.

Usage MVC

public class GreetingPagePresenter : IGreetUseCaseOutputPort, IGreetingPagePresenter {

	private string _greeting;

	public void DisplayGreeting(string message) {
		_greeting = message;
	}

	...

	public GreetingPageViewModel Present() {
		var viewModel = new GreetingPageViewModel();
		viewModel.Greeting = _greeting;

		return viewModel;
	}
}

Registration and execution

// Registration
var resolver = new SelfContainedResolver();
resolver.Register(new GreetUseCaseInteractor());

var interactorHub = new Hub(resolver);

var presenter = new GreetingPagePresenter();

await interactorHub.Execute(new GreetUseCase("John Doe"), presenter);

return View(presenter.Present());

UseCaseResult and IUseCaseFailure

Howto: Pipeline

InteractR supports a middleware pipeline from 2.0.0 that allowes developers to control the flow of what happends before, after or if a interactor executes at all.

Middleware can perform tasks related to a use case before an interactor executes or after, it can also terminate the pipeline. The letter might be usefull if for example some conditions are not met or a feature-flag is set to off.

As interactors don't produce a return model for what to be displayed, Middlewares cannot manipulate the output directly. However as the OutputPort is part of the method signature the output methods can be called.

Register middleware

You can register 3 types of middleware: Global, Generic and Specific.

Global

By implementing the IMiddleware interface you can register a middleware handler that is running for ALL usecases.
example for handling FeatureToggles

public class FeatureToggleMiddleware : IMiddleware {

	private reaodnly IFeatureTogglesService _featureTogglesService;

	/// ...

	public Task<UseCaseResult> Execute<TUseCase>(TUseCase usecase, Func<TUseCase, Task<UseCaseResult>> next, CancellationToken cancellationToken)
	{
		if (_featureTogglesService.OnFor(usecase))
			return next.Invoke(usecase);

		return new UseCaseResult(false, new List<IUseCaseFailure> {
			new UseCaseFailure(UseCaseOffFailureCode, "Usecase Is turned off")
		});
	}
}

Generic

By implementing the IMiddleware<T> Interface you can register a middleware handler that is running for usecases with a generic type association.

example for handling authorization policies.

public class PolicyHandlerMiddleware : IMiddleware<IHasPolicy> {

	private readonly IAuthorizationService _authorizationService;

	/// ...

	public async Task<UseCaseResult> Execute<TUseCase>(TUseCase usecase, Func<TUseCase, Task<UseCaseResult>> next, CancellationToken cancellationToken)
            where TUseCase : IHasPolicy 
	{
		var authResult = await _authorizationService.Authorize(usecase, usecase.Policy);
		if (authResult.Succeeded)
			return await next(usecase);

		return new UseCaseResult(false, new List<IUseCaseFailure> {
			new UseCaseFailure(UnauthorizedFailureCode, "failed to authorize user")
		});
	}
}

Target specific usecase and outputport combination

public class FooMiddleware : IMiddleware<FooUseCase, IFooOutputPort> {
	public Task<UseCaseResult> Execute(FooUseCase usecase, IFooOutputPort outputPort, Func<FooUseCase, Task<UseCaseResult>> next, CancellationToken cancellationToken) {
		// Do some stuff before interactor

		return next.Invoke(usecase); // remove this to terminate the pipeline.

		// Do some stuff after interactor
	}
}

Register middleware

var resolver = new SelfContainedResolver();
resolver.Register(new FooMiddleWare());

Or you can register the middleware with any Dependency Injection Container and use either a provided resolver or roll your own.

Resolvers

Autofac - InteractR.Resolver.Autofac Build status
Ninject - InteractR.Resolver.Ninject Build status
StructureMap - InteractR.Resolver.StructureMap Build status
Lamar - InteractR.Resolver.Lamar Build status

Roadmap

  • Execute Use Case Interactor.
  • Support for pipelines to enable feature flagging / feature toggling.
  • Support for Global "Catch all" Middleware in a usecase pipeline
  • "Assembly scan" resolver that will auto register interactors in the assemblies.
  • Add more "Dependency Injection Container" Resolvers.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages