The capability to deal with multiple environments is another strength of the WxConfig solution. It basically uses a late-binding approach and, upon loading, checks the environment type of the current system (Docker-compatible, by the way). That way, only values valid for the current environment type will be loaded.
Before diving into the details, I want to share a few more conceptional thoughts here. It is worth noting that WxConfig primarily operates on files for storing configuration values, while many projects I have seen use a database for that. A database offers functionality that I would consider “convenience” in the context of configuration management, but it also has some drawbacks (for this use-case).
Primarily, access to files’ content is always easier than to a database. Yes, there is probably no reasonably conceivable scenario that could not be solved with a database. However, it is easier to e.g. view a file in a terminal session, than starting the database command line client and run a select statement. And especially so from a support perspective, when you don’t need additional complexity.
But even more important are things like scripted operations against such files and version control. Why scripting, you may ask. Well, because I see it as a “last line of defense” to achieve a certain behavior. There will always be use-cases out there, that I have not anticipated. And for those it is possible to augment files and achieve what is needed outside of my solution. Also possible with a database – somehow. But do you really prefer that over an Ant script run from your Continuous Integration (CI) job?
And for version control the advantage of files is probably even more obvious. Yes, you can work with something like Liquibase and effectively have a file representation of your database’s structure and contents. But is that so desirable when you could just have plain files?
But, finally, back to different environments and associated aspects. The approach I have chosen is to dynamically load only those files at run-time that are suitable for the current system. So for all config files there is a check whether they are relevant or not. Those that are, will be loaded and the contents of all files added to memory for use by custom code. That is the whole secret.
To rephrase: Once the initialization is finished all the relevant configuration values are available to the application. Their source (i.e. which files were loaded) is completely abstracted away. It is this separation that makes WxConfig so powerful. And in the meantime that basic approach was also applied to other conditions. Because at its core the environment type is just that, a condition.
So we have regular (=unconditional) file loading and conditional loading. Other conditions currently supported are the type of operating system, the version of the run-time container, a timeframe, and also the hostname. And they can be combined with a logical AND. So you can easily develop a solution on Windows that will later run on Linux. All the specifics can be handled by configuration.
When I came up with the idea more than nine years ago, it was a bit like the obvious approach to take. Since then there have been numerous moments when this decision was re-affirmed by the flexibility I got from it.