Most modern applications today have at least some degree of modularity and customizability. By customizability I mean its simplest form - having one “vanilla” application with standard features and many derived versions adapted with minimal modifications to customer’s needs. It is common practice to use dependency injection (DI) and with it we can influence the behaviour of our application by being able to replace one component with another. But DI alone does not provide everything needed to make the application truly customizable. Each aspect of an application needs to support modularity and customizability in its own way. For example, how do you maintain a customized relational database?
This post is intended to be the first of a series regarding issues and solutions we found developing our own customizable application, a kind of an introduction to the subject so that I can write about concrete solutions to concrete problems, as/when they pop up. Here I’ll give an overview of the general architecture, problems and possible solutions.
Our product is a .Net WinForms line-of-business application. I think WinForms is still the best environment for LOB apps as the others are still not mature enough (like WPF) or simply not suitable (like the web). I would say it’s also good for customizability because it doesn’t impose an overly complex architecture: customizability will make it complex by itself.
As I said, DI fits naturally at the heart of such a system. DI can be used for large-grained configuration in the sense that it can transparently replace big components with other components implementing the same features. It’s generally suitable for assembling interfaces and possibly bigger parts of the business logic. It’s not very suitable for a data access layer: even if you used a superfast DI container that puts no noticeable overhead when used with thousands of records, that would be just part of the solution. A bigger question would be how you can customize queries or the database schema. So, for data access we need a different solution, and I believe that this part of the puzzle is the most slippery since it’s so heterogeneous. Firstly, there’s usually the relational database that’s not modular at all: how do you maintain different versions of the same database, each with part of its structure custom-built for a specific client? (It would be, I suppose, a huge relief if an object-oriented database was used, but this is rarely feasible in LOB). Then, there are SQL queries which you cannot reuse/override/customize unless you parse the SQL. Then the data access classes, etc.
At this level, the required solution actually depends on the application architecture. If the architecture contains just what I described at the beginning, a homogenous “vanilla” application and only one customizing plug-in per client, things get much simpler. For example, in this case it is possible to use SQL scripts to maintain the database schema, just develop a mechanism that remembers what scripts it already executed so that they don’t repeat. This is possible because the scripts will always be run in their chronological order. Contrast this with having several modules with different features: if an existing client wants to add a module to his existing application, you would need to upgrade the database by running the scripts from the module. But the database is already up-to-date for current configuration and the scripts from the new module may be written years ago. For this scenario you’d probably need a smarter solution, and I’m not sure that current migration frameworks can support it… Which sidetracks us to one of the most important things in modularity: integration. Most of the “usual” frameworks employed in applications don’t fully support modularity. You have either to customize and integrate them into your application or write your own equivalents. Which presents a kind of continuity of approach: if you want to develop a customizable application, you have to customize other people’s code.
What about the data layer? Once again, many things depend on the rules you establish. If you prohibit the higher level modules from deleting/renaming structures used by lower modules (which, I suppose, is common sense), you will be able to use SQL queries when needed – and given that SQL is unavoidable for complex queries, they most certainly will be. In this respect, SQL actually supports modularity because you can write a query using tables and columns from the base module and it won’t care if there are other structures added to them in the customization process. C# classes aren’t like that, but C# interfaces are.
For other things – like simple CRUD editing of data, an ORM is the logical choice. There are many ways in which you can add modularity to an ORM – and it seems to me none of those can be done without customizing the ORM. The route we took is to use dynamic ORM mapping to replace basic types (contained in the vanilla module) with customized ones (present in the client-specific customization module). This is possible only if the module hierarchy is simple – that is, there’s single inheritance between basic and customized module classes. If two or more different modules wanted to add something to the same data access class, a different method would need to be employed, and that would probably mean using either dynamically generated classes (reflection emit or code generation) or .Net 4’s dynamic objects.
One thing an ORM allows you to do is customize the queries themselves since they are not text-only but represented by a tree-like object structure. You could provide extension points to allow the derived modules to access this structure and modify your SELECTs or JOINs or WHEREs… With LINQ it would probably need to be done by building the query in stages and calling extension methods for each stage because a LINQ query can only be modified by making a different copy of itself. Other ORMS may prove to be more flexible – for example, modifying a NHibernate QueryOver/Criteria query is absolutely trivial since it allows direct modification of the expression structure.
One thing that using LINQ may offer is automatic integration with smarter UI controls: there are some third-party controls that understand LINQ queries and can accept them as a data source. They then modify them to perform server-side paging (with sorting and filtering) in order to retrieve just the data needed for display… In that case, all the customizations done on the data layer directly carry through to the interface.
For this and other reasons, my company chose to ignore the recommendation of not using data access classes in the business logic or interface layers. If you add a customer-specific field to an existing table, you need to piggy-back it to the appropriate data layer class and it will be transported through the bowels of business logic right to your special overridden method where you can use it. It also goes straight to the CRUD UI and you can get away with just adding a control for this field to the existing form and not changing anything else.
That part sounds deceptively easy - “can you just add another field for me to the existing form, please?” Visual Studio 2002 supported derived controls which could have been used for this, but Microsoft was incapable of getting this feature to work so they simply abandoned it. For this, WPF is somewhat more suitable because it separates visual from logical control structure so that theoretically you can add a control to the form and then have a UI designer person go over it and make sure everything is in place. But, WPF is also good because it’s layout logic is dictated from the inside out – that is, everything is laid out to suit the content while in WinForms the content gets the space allowed it by its parents. So, if you use WinForms you have to get a smart layout control – and customize it. I’m under the impression that the whole WinForms designer should be replaced by something less verbose, for example some kind of a fluent interface. There would have to be an intelligent layout engine underneath it so that it can get everything looking right. But, once you get that (yes I know, this also sounds simple but it should be possible to implement some basic logic) a whole world of opportunities opens, because then we can leave it to the engine to actually choose which control to display for which field, which validation logic to attach to it etc. A fluent UI code would also be much easier to merge in your versioning system than the dreaded *.Designer.cs files.
This is probably enough to illustrate the scope and type of the subject. Since it’s an ongoing research I allowed myself to mention some solutions we employed and some we hope to employ. There’s a lot of other subjects like validation, business rules, data models that support customization etc. I will be working on those and posting my findings here, and these future posts will be more focused because each will be more practical and cover a single topic.