The Facade pattern (http://en.wikipedia.org/wiki/Facade_pattern)
A facade is an object that provides a simplified interface to a larger body of code. A facade can:- make a software library easier to use, understand and test, since the facade has convenient methods for common tasks;
- make code that uses the library more readable, for the same reason;
- reduce dependencies of outside code on the inner workings of a library, since most code uses the facade, thus allowing more flexibility in developing the system;
- wrap a poorly-designed collection of APIs with a single well-designed API (as per task needs).
The Adapter Pattern (http://en.wikipedia.org/wiki/Adapter_pattern)
The adapter pattern (often referred to as the wrapper pattern or simply a wrapper) translates one interface for a class into a compatible interface. An adapter allows classes to work together that normally could not because of incompatible interfaces, by providing its interface to clients while using the original interface. The adapter translates calls to its interface into calls to the original interface, and the amount of code necessary to do this is typically small. The adapter is also responsible for transforming data into appropriate forms. For instance, if multiple boolean values are stored as a single integer but your consumer requires a 'true'/'false', the adapter would be responsible for extracting the appropriate values from the integer value.Configuration equals to code
To my understanding a configuration file is nothing but source code. The fact that it’s format is XML for example and not csharp, doesn’t mean it is less important than the rest of your source code. As a rule of thumb: anything that alters the behavior of your system at runtime is source code. A good example is XAML, I guess nobody will disagree that XAML is source code. On the other side the customers table on your database is not source code. So if we have all agreed that the configuration of your logging facility is source code, I don’t understand why the two patterns above do not apply to configuration files also.Configuration Façade Pattern
Let’s assume that your logging facility has say a complex and over engineered schema like the one that the Logging application block has. How can you shield your app from complexity, both in code and config?- In the few first iterations use the block as it is. Don’t try to upfront decide what features of the logging block you need and don’t rush into simplifying the configuration schema. You run into a serious risk here: you are aborting completely an agile approach and you risk loosing valuable time during the initial iterations with less significant issues such as your logging. In addition such cross-cutting concerns require the full attention of the high level architect. Well guess what: the architect has mainly to worry about the domain in the beginning of a project. Wrong decisions in the domain area cannot be easily reverted. the most important think in software development is to build a creative collaboration between technical and domain experts to iteratively cut ever closer to the conceptual heart of the problem.
- Once you have delivered some bits and you have the team aligned and running it’s then time to start taking care of cross-domain facilities. Make a thorough investigation of what features of the logger you currently use. Most likely you will not need any more features from the block. Then based on your findings create a simple interface (something like an ILoggerFacade) that abstracts all you need from the logger. In addition create a simple xml schema to define the configuration you need for your logger. Try to keep it simple and as flat as possible.
- Keeping the log configuration schema simple might be challenging. You as an architect might have problems with dev leads or crazy devs who cannot think out-of-the-box. They will argue that you need to leave a “window of opportunity” open for the future. I always answer back that the widest window of opportunity is simplicity.
- A web app needs to provide a configuration façade flexible enough to allow IT staff to alter it. I believe that the config tool that ent lib provides is a developer tool not something that IT can really utilize. The existence of the tool cannot hide the complexity of the xml schema.
- On the other hand a desktop app needs to be able to be configured within the main user interface. You cannot expect your user to use another tool (say the ent lib config tool) to twist the log settings. Further more if things get really bad and your you are providing end-user support, then you should make sure that your support staff is in a position to guide the user through a simple process to change some settings.
Configuration Adapter Pattern
Let’s assume that you need to use a different log library. If you have a simple logger façade then inferring an adapter to do the switch is fairly simple. Don’t forget, the configuration. You also need to deal with config files. If you have a good and simple configuration façade then making a configuration adapter is also very simple. The problem I often have when changing from one library to another is all configuration. Don’t “vendor lock” your self. Don’t directly use the ent lib configuration. It will not be easy to change. Believe me.3rdparty cherry picking / consistent configuration
There is no question that you need to have the possibility to cherry pick your 3rd party. Example: use logger from one vendor and caching from another. The ent lib has being designed to be cherry picked…. except the configuration. Why? Well because every 3rd party has a different configuration schema. Again: think your users… they have to be confronted with so many different xml config schemas. It doesn’t look consistent. It’s even confusing for developers. In addition your own app will need some kind of configuration. How many schemas are you going to maintain? Another issue is that in some cases you need the flexibility to play with config files at build and install time. The more files you have the more difficult and risky this process becomes.StockTraderRI (a simple example)
The bla – bla – blabity above is pointless without an example. Instead of making my own sample, I have chosen to demonstrate the configuration patterns inside the latest (V4 drop 10) StockTraderRI application that ships with Prism. What we will do is replace the log configuration with our own adapter. The prism team has done an excellent job isolating the code from the logging block in this sample.public class EnterpriseLibraryLoggerAdapter : ILoggerFacade { #region ILoggerFacade Members public void Log(string message, Category category, Priority priority) { Logger.Write(message, category.ToString(), (int)priority); } #endregion }The immediate benefit we have from this simple adapter (could also call it facade) is that out of the 13 projects this solution has only one project needs references to “Microsoft.Practices.EnterpriseLibrary.Logging.dll”. This is simply beautiful design! Now we will do mthe same for the configuration.
public class EnterpriseLibraryLoggerAdapterConfiguration : ConfigurationSection { public Configure() { var builder = new ConfigurationSourceBuilder(); builder.ConfigureLogging() .LogToCategoryNamed("General") .SendTo.FlatFile("General Log File") .FormatWith(new FormatterBuilder() .TextFormatterNamed("Text Formatter") .UsingTemplate("Timestamp: {timestamp}...{newline})}")) .ToFile(FileName); var configSource = new DictionaryConfigurationSource(); builder.UpdateConfigurationWithReplace(configSource); EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource); } [ConfigurationProperty("fileName")] public string FileName { get { return this["fileName"] as string; } } }I have kept the sample simple so the only parameter I configure for now is the file name. It’s not difficult to add more! Next you need to add the new typed section. Note the goodness here! You don’t need to add complex and loaded sections from ent lib. You have total control!
<configuration> <configSections> <section name="logAdapter" type="StockTraderRI.EnterpriseLibraryLoggerAdapterConfiguration,StockTraderRI"/> <!--more sections follow-->You also need of course to specify the settings for the section.
<logAdapter fileName="general.log" />Somewhere you need to instantiate the ConfigSection in code and call the configure method
var section = (SomeConfig)ConfigurationManager.GetSection("logAdapter"); section.Configure();This is it. Only for comparison I will show you what used to be inside the sample in respect to logging. I call this kind of configuration “THICK GLUE”. Don’t infer glue inside your application. Believe me it’s not a good idea. By the way: the glue is not harassing only you. It causes more pain to the patterns and practices team. Just imagine: they have to support all that, they have lost the right and the freedom to move on!
Log Format tips
If you are developing an application that is a web app or if you provide support for your desktop app the read the following lines very carefully.The format of your log files is also an interface! You cannot change it anytime you like. Log files are usually read by IT staff and 1st level support staff before the ticket arrives to you as a bug. What most organizations do is attach log files together with the issues/bugs in order that they can search later in time (kind-of knowledge base). In addition you need to provide instructions to IT and support staff about how to interpret the log files at a high level. This is why I believe that the possibility to alter the format is a super-flexible way via configuration can be very tricky. So unless you have concrete requirements that formats need to change by configuration… hard code a simple format.
Conclusion
The ideas mentioned do not only apply to p&p libraries. WCF configuration is another example. I use WCF extensively. I have noticed that from all the noise inside the wcf config files (especially the client ones) nobody ever touches anything if the developer is not present. So what I do with WCF config files is exactly the same. Configuration Facades and Configuration Adapters!I used the logging block of p&p as an example because I simply love the work and effort of this team!
Feedback
That’s all folks! The drop for the first iteration of this paper. Please send me feedbackI will post soon another related idea that I call configuration injection. It’s basically the idea of injecting typed configuration at a module level using either Unity extensions or the prism module catalog. Stay tuned.
Fixed some typos...
ReplyDelete