Cascading Configuration Design Pattern

I have not been able to find simple plain-language descriptions of what the Cascading Configuration Design Pattern is. And really not that many complicated descriptions either. 

Basically, this pattern is used to simplify very complicated configuration problems, by simplifying what would be a matrix of configuration decisions into a “cascade” of configurations using a hierarchy.

This easier to understand in the context of the most commonly used implementation of this pattern, Cascading Style Sheets (CSS). This is for people who would like to understand the general concept, but if you want, you can just study how CSS works and you get the general idea.

The first is to imagine a “configuration list” where you have common items that are choosable settings (i.e. this is a “configuration” design pattern after all). Which means you have a bunch of items, that can have different things configured about those items. Those can be elements of a webpage, details in a software or data specification, Firewall rules in an access control list or a purchase list for a fleet of vehicles. All of these are examples of  a “configuration matrix”. If we are lucky enough to have a configuration matrix that only requires 2 dimensions to display, we can look at as a “configuration table”. For now, do not get intimidated when I say “matrix”, you can mentally short-cut that to “multi-dimensional table” or perhaps even simpler “really complex table”.

We are going to use an ordering sheet for a fleet of vehicles in our example configuration matrix:

Item: Vehicle Item: Type Config: Color Config: Transmission Config: Windows
Ford Mustang Car Red  Automatic Automatic
Ford Focus Car Red Automatic Automatic
Ford Fiesta Car Red Automatic Automatic
Chevy Corvette Car Red Automatic Automatic
Chevy Volt Car Red Automatic Automatic
Honda Civic Car Red Automatic Automatic
Honda Accord Car Red Automatic Automatic
Ford F-350 Truck Blue Automatic Automatic
Mack Granite Truck Blue Manual Automatic
Mack Titan Truck Blue Manual Automatic

Now, the above table can defined as a data table. Explicitly defining each individual configuration items. They are always vehicles of some type. But you can choose the following items:

  • Color
  • Transmission Type, either Automatic or Manual
  • Window Mechanism, either Automatic or Manual

Now, if you look at the table above, you notice something pretty quickly. There is not that much information there. In fact, rather than having this whole table, you could just tell your fleet manager some simple english sentences about how you configure your vehicles. You could say

  • We never get manual windows. Who needs that noise?
  • All of our cars are red with automatic transmission
  • All of our trucks are blue.
  • All of our trucks have manual transmission, except for the Ford F350, which is automatic

Now why is this set of rules better than the data table? If you imagine a fleet with 200 more models of cars, or 100 different types of semi trucks, you would have a data table with 300 different elements. But as long as the basic rules remained the same, the english version of the configuration is much easier to work with. So you either have a configuration system with 4 rules, or 300 data points in a data table. When you consider that many configuration problems are multi-dimensional (i.e. n-dimensional sparse matrix rather than a 2d table). The benefits having a sparser configuration mechanism become obvious.

But we should note that the english rules have lost no information from the configuration matrix. That basically means they are the same thing, just in two different representations.

The only problem that we have to solve after this is how to ensure that the rules that we have currently written in english are machine-readable. First there is the data encoding standard, and for that we might choose YAML, XML, JSON or any of the other standards that are good for generically encoding hierarchical data. You want a data standard that is both readable by humans, and potentially automatically processable by computers. But the concept is not limited to explicit use inside data formats. You can use this to structure configure data in documents or spreadsheets. If you wanted the best of both worlds, you could use something like Markdown, which is halfway between a data language and a document system.

While you can implement things in very different ways, it is important to keep this rule solidly in mind.

To work, the configuration cascade should be convertible in one and only one way into its corresponding configuration matrix.

If you break that rule. The usefulness of a configuration cascade is completely lost.

So lets recode the english rules above as a simple yaml cascading configuration:

Vehicle: 
    Window: Automatic
    Color: Red

Vehicle-Type:
    Truck: 
         Color: Blue
         Transmission: Manual

    Car: 
         Transmission: Automatic

FordF350: 
    Transmission: Automatic

If you are familiar with CSS, you can see how close this to just applying CSS to another data model. Which is basically the idea of this design pattern.

For everyone else, lets talk a little about what is going on. Using this yaml file, you can recreate the configuration matrix above, by applying the configurations by inheritance in the order of specificity.

Which is to say, the more specifically targeted the rule is, the higher priority it is given. Every thing on the configuration list is a vehicle, which means any configurations under the “Vehicle:” section will apply to the entire configuration. That is awesome because it lets define the fact that we never want to have manual windows (or always automatic windows) easily. We also define the fact that most of our vehicles have the color of red safely at this level.

All of the trucks are blue, and we have a rule that says that under Vehicle-Type:Truck Color:. All of the car configuration items will inherit their color from the Vehicle, but all Trucks will inherit their color from the Vehicle-Type:Truck Color: definition.

We do not need to define the color of the Car Vehicle type, because it will still be red.

Lastly, the specific item of Ford350 is the only truck that has an Automatic Transmission. So we need to specify that item with the Ford350: line and then set its Transmission: option to Automatic. The Ford350 will inherit everything from the Vehicle.. unless it is overridden by the Vehicle-Type:Truck settings (which is what will make the Ford350 blue). And finally, because it is most specifically set, the Transmission: value is going to apply just to that one item.

Note that it is easier to read these cascades when they are written with the most general definitions at the top, and the more specific rules at the bottom. This eliminates the need to figure out whether rule ordering (i.e. what comes first) is more or less important than specificity (what targets the configuration item most specifically). There are some systems that look like cascades that are actually order-sensitive (i.e. this is how firewall configurations work). But rather than try understand which takes precedence, it is better just to start with the most general at the top of configuration files, and end with the most specific. This is the same best practice (BTW) with order of operations in algebra and programming. You should never write 4 x 4 + 10, because that means that you have to know which comes first, multiplication or addition. Instead, you should make it explicit by writing (4 x 4) + 10 because then you are saying the same thing, in a way that is not ambiguous or has to have a set unwritten (and poorly remembered) rules to resolve.

So that is the Cascading Configuration Design Pattern. I hope you found this helpful.