Chris
Chris

Reputation: 2015

Which layer should read from the application configuration in an N-Tier application?

While working on several projects based on an N-Tier architecture I often noticed that I am not quite sure where to actually read from the configuration.

For example, let's say I have a project with an application layer, a business layer and a data layer. The business layer contains a function PerformImport() which performs a data import from a data source. The first step of this import is logging in to get access to the data from the data source. To do this, the function calls a function Login() which is implemented in the data layer. Should it:

  1. Read the login username and password from the configuration and pass it to the Login() function or
  2. Call the Login() function without parameters and have the credentials read in the function itself?

I can't really think about any reasons for or against the first or the second solution, so I am often not sure what to do here. This same question applies to many other possible situations, such as time intervals, URLs, database names or really anything that could be possibly stored in a configuration.

I was also thinking about reading it in the application layer and then passing it down to wherever the configuration entry is needed, but this would often result in a big list of parameters in the lower layers and just does not seem very efficient at all.

Upvotes: 0

Views: 786

Answers (1)

Maarten
Maarten

Reputation: 22945

My answer will assume that you are using Dependency Injection.

My usual method to deal with this, is to define a Settings class next to the Implementation class. Register this Settings class in the DI container, and inject it in the Implementation class.

An example.

Lets assume we have a service which is defined by this interface.

public interface IMyService
{
    // snip for brevity
}

And we have the implementation of it somewhere.

public class MyService : IMyService
{
    // snip for brevity
}

Lets say that the service needs some settings. So define the settings class.

public class MyServiceSettings
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public int TimeOutInSeconds { get; set; }
}

Lets inject this Settings class into the Implementation.

public class MyService : IMyService
{
    public MyService(MyServiceSettings settings)
    {
        this.settings = settings;
    }

    private readonly MySettings settings;

    // snip for brevity
}

Now we can use the settings in the implementation whenever we want.

Now we need to register the Settings class in the DI container. Lets assume we have a container, and the IMyService is already registered. Now add the Settings class there.

public void CreateContainer()
{
    var container = new Container();
    container.RegisterScoped<IMyService, MyService>();

    var myServiceSettings = new MyServiceSettings();
    // TODO: Set values from configuration file, or a keyvault, or Azure Devops Variables, etc

    container.RegisterInstance(myServiceSettings);
}

Now you have all the parts needed to use the settings wherever you need them.

Now where you store the settings, is IMHO usually tied to the resulting build, e.g. an executable. I do not want my class libraries to retrieve the settings from a database or configuration file, they only consume the settings instances I give them.

This technique is really easy to implement if you use the ASP.NET Core Configuration abstractions as described here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0

Upvotes: 2

Related Questions