5

My windows service is neatly separated into 5 components - each with their own interface and pulled together by a bootstrapper. One of my requirements is to provide configuration options in my app.config for the different pieces of the application.

I started with the following interface:

public interface IConfiguration
{
    /// <summary>
    /// The amount of time database records are allowed to be in the system before they're removed.
    /// </summary>
    TimeSpan OldSignalValuePurgePeriod { get; }

    /// <summary>
    /// The lowest log level that will be written to the log file.
    /// </summary>
    LogSeverity MinimumLogSeverity { get; }

    /// <summary>
    /// The period of time to wait for a signal metadata response before re-requesting it.
    /// </summary>
    TimeSpan SignalMetadataRerequestPeriod { get; }
}

which I'd planned to pass a constructed instance of to each of my components requiring configuration. The thing is, each of the above properties is a configuration option for a different component. There's a very good chance I will be adding some more properties at a later date to this interface. Obviously I'm populating the properties of the interface in the implementing object's constructor by reading the app.config file.

What I'm struggling with is this: since each component doesn't care about the configuration options of the other components, how can I separate this interface into pieces specific to each component that isn't a pain to construct, and one that Jimmy will understand immediately?

I've thought of doing something like this:

public interface IRepositoryConfiguration
{
    TimeSpan OldSignalValuePurgePeriod { get; }
}

public interface ILoggingConfiguration
{
    LogSeverity MinimumLogSeverity { get; }
}

public interface IDataManagerConfiguration
{
    TimeSpan SignalMetadataRerequestPeriod { get; }
}

public class ConfigurationImplementation
    : IRepositoryConfiguration, 
      ILoggingConfiguration, 
      IDataManagerConfiguration
{

}

where each component takes an instance of their own configuration interface, but I feel like that is overkill and starts to get unmanageable.

Input?

  • If your component "A" needs, for example, a TimeSpan for OldSignalValuePurgePeriod, why do you have to pass an interface for this one value, providing a function which delivers this one value? Why don't you just pass the value (which is, I guess, a constant, since you mentioned its origin, the app.config file)? Using interfaces for this purpose seems to me a little bit overeingineered – Doc Brown Aug 24 '15 at 16:36
  • 1
    It's one value right now. When the customer inevitably wants other configuration options (this customer is notorious for this) or when I want to add others (maybe I'm notorious too?) an interface makes it way easier to provide all the relevant options. The shape of the constructor for one of the objects is this: public DataManager(IRepository repository, ILogger logger). I'd like to only add one more constructor parameter if possible instead of one for each option. It also makes it way easier to mock during unit testing. – Brandon Aug 24 '15 at 17:17
7

As long as all your configuration options come from a file, using interfaces does not bring you any benefit, since your configuration file can only provide fixed values. Even if you have to deal with more than one option per service, I do not see any convincing reason for using interfaces. You can still utilize simple "data objects" for the configuration and create a configuration class like this one:

public class Configuration
{
     public Configuration()
     { /* logic for getting the values from config file */}

     public RepositoryConfiguration RepositoryConfiguration{get; private set;}
     public LoggingConfiguration LoggingConfiguration{get; private set;}
     public DataManagerConfiguration{get; private set;}
}

The data objects for the individual configurations of your services can simply look like this:

public class DataManagerConfiguration
{
    public TimeSpan SignalMetadataRerequestPeriod { get; private set;}
    // ... add more values here
}

The DataManager constructor will then look like this:

 public DataManager(IRepository repository, ILogger logger, DataManagerConfiguration dmc)
 {...}

As you see, still one additional parameter to the ctor, regardless of how many values are inside the class DataManagerConfiguration. And providing config values for testing purposes will typically result in less boilerplate code than creating a mock interface implementation.

Some remarks:

  • not coupling the different services to one big configuration class is probably a good idea, regardless of your decision for data objects or interfaces

  • if the data objects encapsulate just one integral type, you can even omit the "wrapping" data class as long as you do not really need it.

2

So you have a 5 neatly separated components, which I suppose you can reuse later with different code. Yet you thinking of having a single interface shared between those components, which will provide a bunch of irrelevant config properties, e.g. we can assume that 80% of properties will not be relevant for each of the component.

Doesn't seem logical to me... does it to you?

@Doc Brown has a good point - having an interface for configuration is generally an overkill, unless you're actually having a complex configuration pulling from different data sources/doing some processing.

2

If I could tell everyone who tackles the problem of injecting values from configs into a DI paradigm one thing, it would be this:

You don't have to put all config access through one monolithic dependency. Just because your settings all come from one place under the hood doesn't mean they all belong together, and that you have to persist the config as one giant object all the way through your layers.

I would recommend starting simple and injecting values directly from the config via your bootstrapper. Then if you find a group of several settings you find yourself injecting repeatedly throughout your objects, put them in a class that the bootstrapper populates from config and injects, similar to Doc Brown's answer. However, I would never group them under an IConfiguration. You just don't need to.

  • +1. This is where I wish I could assign two answers. Both you and Doc Brown are correct. I like his code and I like your explanation – Brandon Aug 27 '15 at 0:56
0

I think it makes perfect sense to group multiple configuration options into an interface, when they obviously belong together. But as long as you only have one option per domain, it seems indeed overengineered. However as soon as you get multiple options concerning the same domain (e.g. three options about logging) then you should move them to their own nested interface.

When you have LOTS of options, you might want to compose the different domains explicitly in a IConfgurationWrapper * to avoid overloading your ConfigurationImplementation with too many members.

public interface IConfigurationWrapper{
  IDataManagerConfiguration DataManager {get;}
  ILoggingConfiguration Logging {get;}
  IRepositoryConfiguration Repository {get;}
}

(*) I'm quite sure there's a better name than "*Wrapper"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.