Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

configuration settings and IoC

I use IoC (DI) approach and usually have parameters, which are being read from configuration settings (i.e. connection strings, static values etc) by the lowest layer (DB layer etc). What is the best way to do it?

  1. Read directly in this the lowest layer, i.e.:

    string sendGridApiKey = ConfigurationManager.AppSettings["SendGridApiKey"];
    

It works, but need to add also this key to config file of unit test project. Also, assembly depends on configuration file

  1. Read it in the highest layer (i.e. web application) and throw as parameter from the all layers? It will work, but all middle layers will get parameters, which are not used (so, they will be depended on things, which are not used).

Also there is a problem when different implementations of the the lowest layers can require different parameters. I.e. SendMail1 can require SMTP/login/password, but SendMail2 can require only ApiKey, but SendMail1 and SendMail2 should implement the same interface. So, it creates difficulties to use approach #2

like image 955
Oleg Sh Avatar asked Jan 01 '26 06:01

Oleg Sh


1 Answers

Neither approach you've outlined works well - first (read configuration in services) prevents unit testing as you've mentioned, second (pass config from top level) requires knowledge of all possible implementations of each service by top layer.

I like approaches that rely on DI container's knowledge of both configuration storage and types of objects registered for each interface:

  • pass configuration during registration time - i.e. if container supports registering factory methods such factory method can read configuration and than invoke particular constructor of concrete service

    // constructor: publc ConcreteServiceX(int setting1, string setting2)...
    container.RegisterFactory<IServiceX>(
        container => return new ConcreteServiceX(42, ReadSetting("X"));
    
  • register configuration for each of the service in container as class/interface

    // constructor: publc ConcreteServiceX(IConcreteServiceXSettings settings)...
    container.RegisterType<IService,ConcreteServiceX>();
    container.RegisterInstance<IConcreteServiceXSettings>(
         new ConcreteServiceXSettings(42, ReadSetting("X"));
    

Both approaches localize knowledge of configuration system to one place (container configuration) and allow easier unit testing of each service (no dependency on type of configuration storage) as well as higher level objects (no need to know any settings for services).


Note: sample use Unity-like syntax, adopt to container of your choice

like image 119
Alexei Levenkov Avatar answered Jan 03 '26 19:01

Alexei Levenkov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!