SourceForge.net Logo

Home
StructureMap on SourceForge
Basic Architecture
Concepts
API Documentation
FAQ
Configuration Schema
    Memento Sources
    Node Normalized Xml
    Attribute Normalized Xml
    Attribute Usage
    Instance Lifecyle Scoping
Configuration Management
    StructureMapDoctor
    StructureMapExplorer
    deployment Task
    verification Task
    ValidationMethod Attribute
    Other NAnt Tasks
Troubleshooting
Singleton Injection

Configuration Management

Almost every nontrivial software system requires some sort of configuration information stored outside of the compiled code in a mutable format.  An application in development is almost always running in multiple environments, i.e. development, testing, build, or production servers.  We generally need the ability to “point” an application at different databases, web services, or file paths based on environment. 

Hard coding this type of information into the code base itself is just not feasible.  I was briefly involved with a mission critical application at a Fortune 500 company that put a database connection string into a constant field in all the database access classes.  Prior to promoting the code to the testing or production environment a developer would manually change the connection string to the proper database and re-compile the code.  Guess what happened when new team members forgot to make the change?

Configuration with StructureMap

A side-effect of Dependency Injection with StructureMap is that configuration properties are pushed into objects instead of the objects pulling information from an external file. This crucial difference creates two beneficial results.

  1. The “configurable” classes in the application are completely decoupled from any external configuration.  Instead of classes pulling configuration information directly, StructureMap "pushes" in configuration through constructor arguments and setter properties.  Unit testing is easier because object instances can be constructed without an external configuration file, and tested more quickly with multiple values.

  2. The configuration is now tied to arguments to constructor functions on CLR Types (setter injection will be supported in a future release).  The configuration can now be compared automatically to real CLR Type’s and validated with reflection.

Besides being a Dependency Injection framework, StructureMap is now an improved, fully object-oriented mechanism for application configuration.  New in version 0.90 is a pair of custom NAnt tasks to validate configuration at “build time” and to deploy subsets or profiles of a master StructureMap.config file (StructureMap will support MSBuild when it is released).  StructureMap can also diagnose configuration problems from a command line or in the build with an error code that can be cross-referenced with the TroubleShooting guide.  It is even possible to decorate methods in a class to direct StructureMap to call one or more methods during verification to test runtime functions like database connectivity and web service availability.  The goal of this new function is to detect configuration problems automatically in much the same way that a compiler finds syntax problems and unit tests catch errors.  StructureMap can improve a Continuous Integration infrastructure to facilitate dependable, rapid cycling between coding, testing, and deployment.   

The MementoSource abstract class in StructureMap provides a “plugin” pattern for external sources of configuration.  This point of flexibility was intended to allow for storing configuration in central sources like Active Directory, databases, or for special encrypted configuration.  Users can create new MementoSource implementations and plug them into StructureMap.

Continuous Integration

StructureMap was originally built for usage on a heavy client application backed by web services on an application server and a relational database on the backend.  The development team utilized both Test Driven Development and Continous Integration to support a rapid iterative process largely based on Extreme Programming.  The application ran in basically three different configurations.

  1. Development workstations. In development, the web services running on IIS and the client side code both ran on a developer workstation. The client points to “localhost” for all web service calls. In order to work efficiently and avoid database conflicts, each development workstation connected to a different development database schema. It is a luxury, but having isolated database schema’s for each developer and environment can drastically improve a team’s ability to run automated tests. From painful experience, watching the automated build breaking over and over again because someone else is editing the database at the same time is excruciating.
  2. Build server. The Continuous Integration server (CruiseControl.Net & NAnt) also ran the entire application on one server, including client testing. The application ran on the “BUILD” database.  The build server also ran a suite of automated user interface tests that used a slightly different configuration to replace the web service calls with a direct call to the server components.  This was done to optimize the run times of the tests.  
  3. Test server. An output of the automated build was a packaged deployment that could be automatically pushed and installed to the test server. The client configuration had to point to the web services on the test server. The test server itself used the “TEST” database schema.  A similar dedicated configuration was also created for demonstration purposes for analysts and the customer. 

Challenges

The validation and troubleshooting improvements in StructureMap are largely a reaction to the difficulties and lessons learned from the development process described above.

Application code cannot function correctly without the correct configuration, but errors in configuration cannot be found automatically by the compiler and probably will not be found by automated unit tests in the build.  Runtime errors often ocurred when a configuration setting was created or changed.  

Multiple versions of configuration settings must be maintained and deployed correctly for different environments and possibly even different developer workstations.  Problems can proliferate when a new or modified configuration key or section is not propagated to all possible configuration files.  The typical “but it works on my box” situation is often a result of invalid configuration in the testing environment.  A common problem on the original project was the occasional tester client trying to connect to nonexistent services on “localhost.”  The development team lost time troubleshooting the testing environment.  The structuremap.verification NAnt step was created specifically to validate and diagnose a testing (or any other) environment before the testing team used a new build.  Using the validation step within a Continuous Integration build can help spot configuration issues early. 

The structuremap.deployment task was created to reduce the overhead of managing multiple configuration files.   By centralizing all configuration profiles into one file it became easier to manage multiple configuration sets.  If a new plugin type was added to the system, only one file needed to be changed.  The build process would replicate a subset of the master configuration file each profile and deployment target ("client" or "server").  

A dependency on external configuration increases “friction” in automated unit testing. Test Driven Development (TDD) and its close cousin Continuous Integration (CI) succeed when coded classes are easily instantiated and can function in relative isolation. Unit tests should also reveal the intent of the code being tested as explicitly as possible. A class that pulls information from a configuration file is harder to test because of the overhead of setting up and copying testing configuration files around in post-build steps or automated build files. In addition the unit test is less understandable because it is pulling input data from a file external to the unit test. Simply put, a dependency on configuration of any kind will make TDD more difficult.