Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should Autofac modules register their own dependent modules?

Consider I have an app that uses three libraries, lib1, lib2 and lib3. In each of the libraries I have implemented a Module that registers the dependencies that are implemented in that library.

Some of these implementations have their own dependencies, for instance, lib2 and lib3 might both need some implementations that exist in lib1.

My question is, do I let the module in lib2 and lib3 register the module in lib1 as part of their Load implementation? Is this going to register that module twice if my app registers the modules of lib2 and lib3?

Or do I refrain from letting a module register another module, leaving it up to the app with the drawback that some registrations might be missing at startup?

like image 547
Dave Van den Eynde Avatar asked Mar 10 '23 21:03

Dave Van den Eynde


2 Answers

A little late I know but I thought it might be nice to answer the question rather than just quote scripture about how multiple composition roots are evil...

Is this going to register that module twice if my app registers the modules of lib2 and lib3?

Tested with Autofac 4.8.1 and it does:

public class Lib1Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib1Module");

        // Register Types...

    }
}

public class Lib2Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib2Module");

        builder.RegisterModule<Lib1Module>();

        // Register Types...

    }
}

public class Lib3Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib3Module");

        builder.RegisterModule<Lib1Module>();

        // Register Types...

    }
}

public class Program
{
    public void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<Lib2Module>();
        builder.RegisterModule<Lib3Module>();

        using(var container = builder.Build())
        {
            // Do Stuff
        }
    }
}

Outputs:

Registering Lib2Module
Registering Lib1Module
Registering Lib3Module
Registering Lib1Module

You can use the Properties dictionary on an IComponentRegistry/ContainerBuidler (The Module base class internally creates the Container builder it passes to Load with the properties from the IComponentRegistry - source) to work around this and force single registrations for modules, if the Load Method of Lib1Module is changed to:

protected override void Load(ContainerBuilder builder)
{
    if (builder.Properties.ContainsKey(GetType().AssemblyQualifiedName))
    {
        return;
    }
    builder.Properties.Add(GetType().AssemblyQualifiedName, null);

    Console.WriteLine("Registering Lib1Module");

    // Register Types...

}

Then the output becomes:

Registering Lib2Module
Registering Lib1Module
Registering Lib3Module

Obviously if Lib2Module/Lib3Module can then become dependencies of other modules similar code would have to be put into their Load method and similarly if any modules used AttachToRegistrationSource and/or AttachToComponentRegistration and wanted to make sure they were run only once they would also need the check. Alternatively (and probably preferably if this is something you need to do a lot) you could create your own class implementing IModule and do the check within Configure.

I have definitely made use of this pattern in production code to keep the amount of repetition down where I have multiple entry points with their own composition roots (e.g a web api, a web app and a recurring console app) that nonetheless share a large chunk of code and I can live with that making me a persona non grata among DI purists.

like image 74
Ed Askew Avatar answered Mar 30 '23 01:03

Ed Askew


I won't recommend doing registrations inside your libraries. In most case you should have one composition root that will compose all your application.

A Composition Root is a (preferably) unique location in an application where modules are composed together.

This concept is explained here composition root.

By the way, if your register a module multiple time, Autofac will register component multipe time. If you must have module inside your library you should only create module that register component of the library.

like image 31
Cyril Durand Avatar answered Mar 29 '23 23:03

Cyril Durand