Register Now
Member Count: 176,912 - November 21, 2008  [Get Time]
Login
Dashboard > TopCoder Competitions > ... > Component Standards and Practices > Configuration Management
TopCoder Competitions View a printable version of the current page.  
Configuration Management
Added by adamselene , last edited by mrgumbe on Aug 20, 2008  (view change)
Labels: 
(None)

Objective

The objective of this page is to establish architectural best practices for TopCoder component configuration management, to be reflected in future components and to be factored into existing components.

Status

Working Draft

Requirements

Configuration must be standardized across all components. Configuration for any component X within the catalog should be practically similar to configuration for any component Y.

Default configuration implementation must be defined.

Configuration scope must be defined.

Unit tests should test the component's logic and not integration of the component with other components or system dependencies.

Requirements Discussion

There are many configuration approaches components can use, e.g. Inversion of Control, Bootstrapped, Programmatic, etc. Application development and maintenance are greatly augmented when a single consistent approach can be leveraged across the entire application. Through proper design components can defer the configuration approach to the application. Configuration Management implementation and interfaces should be adequately separated to allow for swapping in new implementations with minimal amount of work.

Lessons learned from use of Configuration Manager (CM)

Components had a hard dependency on CM component which meant even if CM was not used it had to be included at compile and even run time. Even components that allowed for alternate means of configuration typically were hard to use without bootstrapping off of the CM. Configuration parameters were inconsistently defined: hard coded parameters often had different scope modifiers and sometimes parameters names were even configurable. Configuration code often dominated the main class of a component making reading the code a difficult, tedious, and time consuming process. Use of the singleton pattern greatly limited the flexibility in alternate persistence mechanisms. Tight coupling of the configuration persistence implementation, the configuration model, and the client components greatly reduced flexibility. Unit tests typically depend on CM setup instead of stubbing, partially because of the difficulty in efficiently stubbing CM. This makes setup and running of unit tests more difficult and the unit tests less powerful.

Recommendations

Decouple the persistence implementation from the configuration model:

  • Configuration API provides a powerful object that models configuration.
  • Configuration Persistence provides file based (XML or Java property) persistence component that provides default persistence compatible with existing files.
  • Configuration Persistence can also provide support for newer file based features compatible with Configuration API.
  • For .NET, Configuration Persistence Manager allows applications to load applications from a persistence store without a hard dependency on the persistence implementation. Current compatible implementations include the file system, and standard .NET Configuration objects. Future implementation could include a database level configuration store.
  • Only code that requires into a top level application should load files from persistence. Any component that does not run as a standalone program should NEVER load Configuration API objects from persistence itself, but rather accept the configuration as constructor parameters, properties, or "Configure" methods.

Component designs will be scored for adherence to the following configuration best practices, unless the component design can provide a compelling justification for deviation. If no such justification exists in the design documentation, then reviewers must require that the design be changed to adhere to these practices or if doing so would create a significant negative impact on the design AND a valid justification for the variance exists, that the justification be added to the design

Design Best Practices:

  • Components are broken into simple and complex categories
    • Simple components do not depend on other components
    • Complex components depend on at least one other component
  • Simple components shall only support programmatic configuration, i.e. constructor or setter of objects required by the component, consistent with other component design principles, like thread safety.
  • Complex components shall support
    • At a minimum programmatic configuration as supported by base components.
    • Use of Configuration API when such usage significantly enhances the design's quality.
  • Programmatic configuration shall use actual meaningful objects and not proxy for these objects with primitives or simple objects that must be translated by the component.
  • Components that use Configuration API shall keep all such usage in an appropriate helper class and or method, such as a builder or factory, which leverages the programmatic configuration and handles any string conversion and configuration of dependant components.
  • Applications shall define any necessary singleton or wrapper constructs for wrapping shared components or limiting access to the file system via the configuration persistence.
  • Only rare cases where a component provides a standalone program (compiles to an exe in .NET or can be run with "java.exe"  for Java components) may load configuration from persistence using either .NET Configuration Persistence Manager or Java Configuration Persistence components. All other components must accept Configuration API objects as inputs.

Unit tests for components shall adhere to the following development best practices with respect to configuration, to the extent that more general best practices exist, this should be read to provide clarification to the general best practices, without overruling them. Where it is not possible to provide unit tests that follow these best practices the developer should so note in an appropriate place in the unit tests' javadocs.

Development Best Practices:

  • Component unit tests shall decouple the dependence configuration components
  • Components unit tests shall decouple the dependence on the configuration (and implementation) of other components
  • If multiple configuration approaches, i.e. programmatic and convenience object are supported, then unit test coverage shall include verification of both configuration approaches, but without injecting dependencies.
  • If the component uses Configuration API, then the Component Specification should include a snippet of XML code from the configuration file used by the application to configure the component.

Nesting of Configuration

Complex Components that use and configure other simple or complex components will keep their child component's configuration as a child of their own configuration. For example, consider the two components "Widget Service" and "Widget Persistence." Widget service uses object factory to create an instance of Widget Persistence, and then calls Widget Persistence's "void Configure(IConfiguration config)" method with the code: "persistenceInstance.Configure(myConfig.GetChild(PERSISTENCE_CONFIG_KEY));"

This will give the child service the correct configuration. Under no circumstances should a component assume that the configuration object passed to it is a parent of its own configuration, and immediately try to retrieve a child object of its own configuration. While this may seem obvious, there are instances in the catalog where both a parent and a child take nesting into account, producing an unnecessary layer of configuration.

Additionally, the parent object should not read a property to give it the name of the child configuration (e.g. "persistenceInstance.Configure(myConfig.GetChild((string) myConfig.GetSimpleAttribute(PERSISTENCE_CONFIG_NAME)));" is incorrect). The child configuration name needs to be a known string set as a "const string" or "final static String" of the parent class.

Open Issues

Definitions

There is an issue with Configuration API. Suppose that I configure a class with ConfigurationObject. This class needs the persistence interface that it's constructed through Object Factory. I must pass a ConfigurationObject to Object Factory. Where do I retrieve the ConfigurationObject for Object Factory? from the ConfigurationObject of the original class? If so then in the entire configuration I must link the CofigurationObject for Object factory in all ConfigurationObjects of all classes that need ObjectFactory.

Posted by fabrizyo at Mar 01, 2008 08:13

I've noticed this problem too, I think there are several ways to handle this. And component size and scale will dictate much of what is appropriate.

Let's say you have ComponentBuilder class and ComponentMain class. ComponentMain is created by ComponentBuilder, through the use of ConfigurationObject. Let's also assume that an interface ComponentDAO defines the persistence for ComponentMain and is passed through a setter.

ComponentMain build(ConfigurationObject config) throws Exception {
    ComponentMain inst = (ComponentMain) ObjectFactory(...); //create instance
    //option 1
    inst.setDAO((ComponentDAO) ObjectFactory(...)); //use child of config with preset name to id persistenc
    //option 2
    ComponentDAO dao = (ComponentDAO) ObjectFactory(...); //just use reflection (no configuration)
    dao.setXXX(...); //have preset rules for setters in the interface and for retriving them from Config
    //option 3
    ComponentDAO dao = (ComponentDAO) Class...((String) config.get...()); //use reflection directly
}
  
//option 4
ComponentMain build(ConfigurationObject config, ComponentDAO persistence) throws Exception {
    ComponentMain inst = (ComponentMain) ObjectFactory(...); //create instance
    inst.setDAO(dao);    
}

I think for simpler components option 4 makes more sense, because you just let the caller who knows how to get the configuration object build the DAO also. (I see DAO as a special case of dependent object since application will likely want to have its own implementations to make sure right DB and schema are supported.)

Posted by bwright at Mar 11, 2008 10:02

Can you provide some discussion about .Net configuration and Application Based Configuration component?

for example, should we write the custom configurationhandlers?

Is sufficient to create the use the same structure of the app.config sections in the IConfigurationAPI? 

Posted by fabrizyo at Apr 05, 2008 14:00

I just updated this article reemphasizing the fact that components should, as a rule, NOT load their own configuration for persistence, but only accept configuration objects from the code that calls them. The only exception to this rule are components that serve as standalone application (i.e. no other code calls them). These include virtually anything that has a "main" method in Java, or compile to an exe in .NET. One example of this exceptional case is the WCF Host standalone application.

 Concerning App Based Configuration, version 2.0 is currently in development. The finished component will contain a ConfigurationSection that allows the user to specify the structure of IConfiguration in a standard manner.

 UPDATE: Application Based Configuration 2.0 is complete and in the catalog.

Posted by mrgumbe at Jun 05, 2008 19:52Updated by mrgumbe