Enterprise configuration management

Almost every application requires some form of configuration information. This information can be as simple as a database connection string or as complex as multipart and hierarchical user preference information. How and where to store an application’s configuration data are questions you often face as a developer.

Any large enterprise application has many moving blocks. They all need to be configured for a proper working of the application. As the application size increases or for scalability the same configuration has to be repeated in different applications. For most applications once the configuration has been changed the application needs to be restarted.

Sample Code (create a blank console project, add json.net nuget)

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;

namespace CM
{
    // configuration management API, 
    //1. allow clients specify typesafe models for configuations
    //2. Store flat data on server (table) which is easy to edit
    //3. can be extended to have inheritance of values (overrides)
    //4. can be extended to lock / unlock certain property by admins etc.
    
    class Program
    {
        //Sample configuration model
        internal class SampleConfigModal
        {
            public SampleConfigModal()
            {
                Address = new Address();
            }
            public string Name { get; set; }
            public Address Address { get; set; }
            public int Age { get; set; }


        }
        public class Address
        {
            public string Street { get; set; }

        }

        // this is how client api will look like
        static void Main(string[] args)
        {
            // sample client code
            var data = new SampleConfigModal() { Name = "Rajnish", Age = 18, Address = new Address() { Street = "Oxley" } };

            // save Configuration
            SaveConfiguration("app", "section", data);

            //get Configuration
            var data2 = GetConfiguration("app", "section");
        }

        // Client side framework api -> call to rest end point
        private static T GetConfiguration(string appName, string SectionName) where T : new()
        {
            var defaultValue = new T();
            var samplePayload = JsonConvert.SerializeObject(defaultValue);
            var payload = GetConfiguration(appName, SectionName, samplePayload);
            return JsonConvert.DeserializeObject(payload);
        }

        // Client side framework api -> call to rest end point
        private static void SaveConfiguration(string appName, string SectionName, T data)
        {
            var payload = Newtonsoft.Json.JsonConvert.SerializeObject(data);
            SaveConfigurationa(appName, SectionName, payload);
        }


        //---------------------------------------- Server Code -------------------------- 
        //-------------- server has no knowledge of configuration structure or model

        private static Dictionary<string, string> storage;

        private static void SaveConfigurationa(string appName, string SectionName, string payload, string enumForHierarchyLevel = null)
        {
            // transformer
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(payload);
            var keyValueData = Flatten(data, section);

            // check if user has permission for level overrides
            // store with proper overides

            // store the flat list in sql or data 
            //| KEY |           |Value|            |OverrideType| - default,sysadmin,appadmin,groups,user etc
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            storage = keyValueData;
        }

        private static string GetConfiguration(string appName, string SectionName, string samplePayload)
        {
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(samplePayload);
            var keyValueSample = Flatten(data, section);
            // update data from sql or data store
            // apply property override rules and get value from overrides if exists
            var keyValueData = keyValueSample.Select(x => new KeyValuePair<string, string>(x.Key, storage[x.Key]));

            //read these
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            UnFlatten(data, section, keyValueData);

            var formatedData = JsonConvert.SerializeObject(data);
            /*
             * {
                  "Name": "Rajnish",
                  "Address": {
                    "Street": "Oxley"
                  },
                  "Age": "18"
                }
             * */
            return formatedData;

        }
        
        // Server side json helper

        private static void UnFlatten(JObject jsonObject, string prefix, IEnumerable<KeyValuePair<string, string>> data)
        {
            foreach (var item in data)
            {
                var keyName = item.Key.Substring(prefix.Length + 1);
                var storageValue = item.Value;
                if (keyName.Contains("."))
                {
                    var keys = keyName.Split('.');
                    var jtoken = (JToken)jsonObject;
                    foreach (var k in keys)
                    {
                        jtoken = jtoken.SelectToken(k);
                    }
                    ((JValue)jtoken).Value = storageValue;
                }
                else
                {
                    jsonObject[keyName] = storageValue;
                }
            }
        }

        private static Dictionary<string, string> Flatten(JObject jsonObject, string prefix)
        {

            IEnumerable jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
            Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
            {
                properties.Add(string.Format("{0}.{1}", prefix, jToken.Path), jToken.ToString());
                return properties;
            });
            return results;
        }
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s