Menu

Software Architecture (2)

Building customizable applications in .Net

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.

Model-Controller pattern for business rules

The model-view-controller (MVC) pattern has an interesting, let’s call it sub-pattern, that could be more broadly used. The basic purpose of MVC is to “clean up” the data (model) and interface (view) by delegating all the in-between “dirty” logic to the controller. The controller is aware of both the data and the interface and knows how to handle both: it controls what is happening in them, but from the outside. The controller, if written correctly, can be reused for multiple types of model or view. If we go a step further, we could program multiple controllers that know how to handle different aspects of the functionality: in this way we very much get add-ons that we can attach onto non-intelligent components and make them smarter. By choosing which controllers we attach, we can customize the behaviour of our application. And because the controller is made to work “from the outside” it is isolated from the internal implementation of the components: this could make it instantly reusable.

But it could be interesting to generalize the “add-on” controller pattern and make it work in other situations as well. One interesting area of application could be controlling data: if you use an Object-relational manager (ORM) to read your data (and if you don’t, you should), you already have your data in the perfect form for this - that is, as objects. Likewise if you use a traditional data-access layer and then create business entities as object-oriented structures. Usually, these classes contain much of the business logic inside and this is quite natural to do because business logic represents the behaviour of the data. But it’s not good for reusability: whether you combine the data access and business entities or implement them separately, the business logic is tightly coupled with data/hardcoded inside data so it’s not easy to customize. It would be nice if we could externalize at least some parts of it, but keep it as close to data as possible. Even better would be if we could split the logic and implement different aspects as different components.

If we attached controller objects to our data, they could take the responsibility of providing its behaviour. We could have different controller types for different types of behaviour (status changes, automatic calculations etc.), and if we instantiate them using some kind of configuration engine (or dependency injection framework), they will be easy to replace and thus make your application highly customizable.

Of course, these “data controllers” sound similar to what rules engines do, only more lightweight. But how much of an overhead would they add? As always, it depends on what we do, but I would say not too much. They would contain the code we already have, with probably one layer of isolation because it would be split into distinct classes. Would the instantiated rules waste memory? Probably some: but when you consider that the ORM instantiates each record as an object and has a complex infrastructure for tracking its changes; that for each data binding there is a couple of objects created in the interface; that there can possibly be a control (which is quite a large object) bound to each field… It seems that it shouldn’t make a big difference.

The most sensitive bit is collections: there we can have hundreds of records with one control (a data grid) bound to them. In this case, the overhead of the rules engine could be significant compared to the overhead of the interface – but not in comparison to ORM overhead. Again, this depends on how we implement the rules: the rule could be optimized to utilize a single instance for the whole collection – this can be done using an IBindingList implementation where the collection transmits events for everything happening inside it… Of course, here it is the collection which incurs the overhead, but let’s say we would use it anyway for data binding purposes.

Obviously, there’s a lot of technical issues here. This is partly because the base framework doesn’t have built-in support for what we need. But, it is to be expected that having an ORM (which can be interrogated for data structures and from which we can receive notifications when data is loaded/saved) generally helps.

Of course, I’m still talking hypothetically here: but I’ve already made a couple of small steps in the direction of having a living prototype (in fact, I’m testing an minimal working something as we – uhm, speak). More on that in another post.

Subscribe to this RSS feed