Search code examples
c#dependency-injectiononion-architecture

Where to put ConnectionStrings in Onion Architecture


I have an Interface IApplicationConfig that basically holds ConnectionStrings like for the Database, Blob Storage and other stuff. For every "executable" Project in my solution, like websites, command line tools etc. there is a concrete implementation of that interface which is configured to be resolved via dependency injection.

My question is, where should I put these IApplicationConfig-Implementations?

Next to my web.config/app.config in each "executable" Project? Or in Infrastructure, as Project1Config.cs, Project2Config.cs etc.?


Solution

  • Preface

    The way I have implemented managing configuration in one of my applications which is structured following the rules of Onion Architecture is as follows.

    In this particular case I'm considering managing configuration (for instance logging as well) as an infrastructural concern, so that means that it's clearly not part of my application core. This is because it's an other layer.

    Philosophy course

    To demonstrate the infrastructural concern please take a look a diagram below:

    Onion Architecture diagram

    * Image linked from the blog of Shawn J Lee.

    Here you can see that logging takes place in the infrastructure slice or layer, I have stated that managing configuration is also an infrastructural concern.

    Now, if anywhere in your architecture if you need to get a hold of your configuration manager implementation, you just ask for your IApplicationConfig in your constructors and get the proper implementation injected as per configured in your favorite DI container/framework. This is called as the Hollywood Principle or better yet Inversion Of Control.

    Enough babbling you say? Now let's get straight to the technological demonstration...

    Implementation overview

    You have asked about where to put actual implementations. I would structure these bits as:

    • Application core layer(s)
      • Domain interfaces assembly
    • Infrastructure layer
      • Configuration assembly
      • Dependency resolution assembly
    • UI layer
      • Commandline assembly
      • Website assembly

    Getting sleepy eh? Let's shift to higher gears now.

    Implementation example

    I will try to illustrate here the actual implementation details in short.

    Domain interfaces assembly

    This is the layer where you put all the interface which the application core -and other layers as well- will work against or utilise.

    Say we have IApplicationConfig.cs:

    public interface IApplicationConfig
    {
        ConnectionStringSettingsCollection GetConnectionStrings();
    }
    

    Configuration assembly

    This is the place where you can have the actual implementation(s) of your IApplicationConfig interface. Having this as a separate assembly is questionable but it's really just an implementation detail, I personally store all my configuration managing implementations here.

    For instance an implementation of IApplicationConfig could be as ApplicationConfig.cs:

    public class ApplicationConfig : IApplicationConfig
    {
        public ConnectionStringSettingsCollection GetConnectionStrings()
        {
            return ConfigurationManager.ConnectionStrings;
        }
    }
    

    Dependency resolution assembly

    This is where you "link" your interfaces with the needed implementations. Here I'm using Ninject and have created a Ninject module for it calling it as ConfigModule, a simple example:

    public class ConfigModule : NinjectModule
    {
        Bind<IApplicationConfig>().To<ApplicationConfig>();
    }
    

    And finally the Commandline/Website assembly

    Usually these are the Composition Root or the entry point of your applications where you compose your object graph. Having said this we now just need to load our Ninject module:

    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
    
        var modules = new List<INinjectModule>
            {
                new ConfigModule(),
                new WhateverModule(),
                ...
            };
    
        kernel.Load(modules);
    }
    

    References you need to add Commandline/Website assembly

    Having the interface contract and it's implementation separated, here you only need to add references to the:

    • Dependency Resolution assembly
    • Domain interfaces assembly

    And now you can use your implementation in whichever layer you want.

    Afterword

    This example has been taken from an actual application of mine, if you disagree with naming things, I agree... it's not an easy thing to do.