TomasEkeli / ToDolittle

Sample todo app using the Dolittle framework
MIT License
1 stars 3 forks source link

error #2

Closed paulvanbladel closed 5 years ago

paulvanbladel commented 5 years ago

Nice sample. Getting error when adding first todo :) Autofac.Core.DependencyResolutionException: An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = CreateItemBusinessValidator (ReflectionActivator), Services = [Domain.TodoItem.CreateItemBusinessValidator], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = OwnedByLifetimeScope ---> None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Domain.TodoItem.CreateItemBusinessValidator' can be invoked with the available services and parameters: Cannot resolve parameter 'Domain.TodoItem.MustNotBeATaskOnTheList notBeATaskOnTheList' of constructor 'Void .ctor(Domain.TodoItem.MustNotBeATaskOnTheList)'. (See inner exception for details.) ---> Autofac.Core.DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Domain.TodoItem.CreateItemBusinessValidator' can be invoked with the available services and parameters: Cannot resolve parameter 'Domain.TodoItem.MustNotBeATaskOnTheList notBeATaskOnTheList' of constructor 'Void .ctor(Domain.TodoItem.MustNotBeATaskOnTheList)'. at Autofac.Core.Activators.Reflection.ReflectionActivator.GetValidConstructorBindings(IComponentContext context, IEnumerable1 parameters) at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable1 parameters) at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters) --- End of inner exception stack trace --- at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters) at Autofac.Core.Resolving.InstanceLookup.Execute() at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable1 parameters) at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable1 parameters) at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable1 parameters, Object& instance) at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable1 parameters) at Dolittle.Commands.Validation.CommandValidatorProvider.GetBusinessValidatorFor(Type commandType) in /Users/einari/Projects/Dolittle/DotNET.SDK/Source/Validation/Commands/CommandValidatorProvider.cs:line 87 at Dolittle.Commands.Validation.CommandValidator.ValidateInternal(ICommand command) in /Users/einari/Projects/Dolittle/DotNET.SDK/Source/Validation/Commands/CommandValidator.cs:line 61 at Dolittle.Commands.Validation.CommandValidator.Validate(CommandRequest command) in /Users/einari/Projects/Dolittle/DotNET.SDK/Source/Validation/Commands/CommandValidator.cs:line 42 at Dolittle.Runtime.Commands.Validation.CommandValidators.Validate(CommandRequest command) in /Users/einari/Projects/Dolittle/Runtime/Source/Commands.Validation/CommandValidators.cs:line 34 at Dolittle.Runtime.Commands.Coordination.CommandCoordinator.Handle(ITransaction transaction, CommandRequest command) in /Users/einari/Projects/Dolittle/Runtime/Source/Commands.Coordination/CommandCoordinator.cs:line 82 Microsoft.AspNetCore.Mvc.Internal.ContentResultExecutor:Information: Executing ContentResult with HTTP Response ContentType of application/json Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action Dolittle.AspNetCore.Commands.CommandCoordinator.Handle (Dolittle.AspNetCore.Commands) in 615.6418ms output Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 626.3526ms 200 application/json The thread 0x1dac has exited with code 0 (0x0).

woksin commented 5 years ago

Hey again, @paulvanbladel. Based on your error stack there I assume that your problem is related to the DI resolving of Rules based on the fact that it ocurred on the CreateItemBusinessValidator validator class, which usually use our Rules-engine.

The implementations of the IRules classes aren't hooked up by Autofac, which means that it doesn't have a reference to the project where the implementations are. By looking at the Core project here, and the Core.csproj, I see that it does not have a reference to the Rules project. By putting in a reference to Rules.csproj it should hopefully work.

This is an error in the sample code and should be fixed, thanks for spotting it, @paulvanbladel :)

@TomasEkeli

TomasEkeli commented 5 years ago

Thanks for this - I'm looking into it :)

paulvanbladel commented 5 years ago

@woksin @TomasEkeli Great ! I confirm it works like a charm after putting in the project ref.

paulvanbladel commented 5 years ago

Not hindered by too much intimate knowledge of the framework, but... is is really necessary that the bounded context solution is split into several .net projects? Maybe simple folders inside one project would work as well? In one project, the above problem with be a non issue.

For me, a project structure is in instrument for streamlining deployment not architecture. A design suffering from poor separation of concerns will not be fixed just by separating things into separate .net projects. Luckily, the dolittle separation of concerns can serve as a textbook example:) Separation in projects is necessary when there is 'common' functionality, but I would rather think that cross-referencing assemblies between bounded contexts is an anti-pattern. In the current setup, that is possible because there is project split up.

Of course, you might have 99 other good reasons for doing it in the way it's currently done :)

TomasEkeli commented 5 years ago

This is a great question, and one we've actually considered in some depth. Essentially we separate the projects to keep the "Read" and "Write" sides in our architecture apart. To allow for commonalities we use Concepts and Rules. I've been thinking lately that we could call "Domain" "Write" instead to illustrate this better.

We don't allow any direct references between these main parts of the architecture, but (and there's always a but) that leads to problems with business validation. To validate commands you often need to check what is in the system, and that responsibility falls to the read-side, not the write-side where the command validation is done.

We've chosen a convention to alleviate this by defining Rules in the framework, and having the write-side depend on them. The actual implementation of the rules can then live in the read-side, where they make sense. The downside of this is that the IoC (and by extension the core-project) have to be able to tie these together at runtime. This is why the core needs a reference to the Rules project.

We are creating tools to allow the developer to view the features she is making in a coherent way, separating her from where the files actually need to live - as we don't feel a file-based hierarchy is necessarily the best in every case.

You're giving us some impressive feedback, @paulvanbladel - thanks for that!