The bigger the options toolbelt, the more power we have.

What is one of our main goals when working on a solution? Isn’t it to provide simplicity, decoupling and separation to a certain degree, that it will not overcomplicate our solution but keep the code clean enough to not be sullied by all sorts of overhead requirements. And this is always a balance that we are trying to achieve. This enables us to focus on solving domain problems, instead of us looking at code that can be convoluted with all sorts of none domain-related structural requirements.

With the ability to extract configuration and validation of the same configuration, we reduce the coupling in all layers that require it. We can encapsulate its validation we provide and always have a valid state of our configuration at any point of our solution. Why do we want to appreciate this little nugget of simplicity that it can bring us? Well, it does one thing amazingly it separates configuration concerns. And by doing this we chip away another thing of the block that enables us to have a cleaner problem-solving space.

…Sometimes science is more art than science….

Rick and Morty

Photo by Eugenio Mazzone on Unsplash

So Many IOptions

How does IOptions help in the separation of configuration concerns

What we quite often need is to provide configuration information on a solution-wide level. This configuration can be either an appsetting.json, environmental variables, or any other configuration. And this information can be basically loaded into a strongly typed class. These classes can be bound to the key-value pairs of our configuration. By using IOptions we can insulate our core problem-solving space, by separating concerns, and at the same time provide injectable loose coupling of the way we introduce this information. The interface does not support the reading of data once the application has been started and is registered as a singleton, which in the end can be injected into any lifetime. As an example, we can use database setting information that is something we use quite often:

In this class, we have a simple database settings related section of the appsettings.json class. What we need to keep in mind is that this class needs to be with a public parameterless constructor and contain public read-write properties(which are used to be bound).

This would bind to an appsetting.json settings as such for example:

What we need to consider is that the properties that don’t have public properties, will not be bound. At the same time sections or keys that are not in the class or have a different name, will not also bind. Although this might be obvious, we still must consider it.

For us to use this class in our configuration we would need to bind it in the ConfigurationServices method in the Startup.cs class.

Once the DatabaseSettings configuration has been registered and an instance has been created, we can use said configuration in our solution as in the following example:

This as in the example, we can see that can be injected anywhere in the solution and used based on per need.

Something that can be beneficial if you have multiple sources of configuration and a hierarchical decision from where these values are retrieved can be packed into the configuration class. Or at the same time, default values can be defined.

Extra Use of IOptions

A neat way that this can be used, is with Feature Flags. Where feature flags by themselves can be used in quite a lot of ways. Just some of those can be when introducing new functionality and having the option to switch it on or off based on need, or in case of extracting old functionality into a new implementation. This will enable us to have the option to switch step by step safely into the new implementation with careful use of the strangler pattern(same as any pattern, the strangler pattern can be used in multiple different ways). What needs to be considered here, is that IOptions is a singleton registered and created as an instance on application startup. In these cases we would need to be able to change based on changes on those flags, in this case, this configuration strongly typed classes can be bound with IOptionsMonitor or IOptionsSnapshots. Each of those brings different value, based on the need on when our application would need to react upon, either configuration changes or once per request changes.

At the same time, we do have the option of providing validation of the said configuration information provided:

Wrap Up

All in all, this strongly typed way of using configuration is a great addition. It provides a way for us to safely process and validate the configuration, and at the same time separate the way we load it and validate it outside of the remaining part of the problem space, where it leaves the space clean from configuration concerns. Once you are aware of the approach it brings a bit more clarity on how configuration information can be used and provided to our applications.