Building Application Level Abstractions

Posted on Thursday 14 October, 2010 in programming and domain-driven-design

The abstractions we build to solve a problem are just tools and, as such, should be created for utility not to match reality. A problem can be solved in any number of ways and when delivering software it is no different. You should tailor the abstractions you create to solve your specific problems.

When an application address a number of problems instinct often suggests a single abstraction that either models the real world is as “flexible and generic” enough to solve any problem in the domain. However in trying to unify the solutions in either of these ways leads introduction of complexity. Generalising a solution makes it less tailored to solving an specific problem. The result is often a confused code base, an overloaded language used to describe problems and (perhaps more importantly) users having to deal with the limitations of them system.

Bounded Contexts

In his book, Domain Driven Design, Eric Evans refers “bounded contexts” as being logical groupings around parts of an application focused on solving a distinct problem. In these bounded contexts a common language and understanding of the domain often exists. Acknowledging the existence of these contexts and introducing distinct boundaries allows focus on delivering solutions that are tailored to individual problems. The resulting clarity from appropriate abstractions and communication in a common language allows for more effective solutions. It often manifests itself though much simpler code, faster delievery and a solution for the user that not constrained or impeded by the system limitations.

Implications of a Single Abstraction

Recent experience of how effective this technique was shown in delivering an application that provids price comparisons for various home utilities. There are where distinct parts of the system: the import and administration of data, comparison and pricing of data relevant to a user, and presentation of this data to a user.

Previously there was a single model and language for each of these areas and even in such a simple application it had a profound effect. For administrators it was a thoroughly frustrating experience to import the data as it took them hours to complete a simple tasks. For developers it resulted in a code base in which parts were untouchable and a system that was inflexible and difficult to make anything but the most superficial of changes in. For an end user data was often out of date or a system that wasn’t responsive.

Results of Modular System

Given the opportunity tackle this legacy system allowed the introduction of bounded contexts around the three areas and to evolve within them seperate, tailored, abstractions. This quickly achieved some impressive results.

Having a specific language around data administration and an abstraction that was relevant to the task (not required conform with over arching model) allowed for a number of profound changes. The users had a system that was simple to use and solved their problems. It reduced the time for common tasks down to minutes from hours and days. The abstractions of the other parts of the application did not leak into it resulting in a simpler code base in which changes and improvements could be be made quickly and by anyone on the team. A common language with which to discuss issues with the users allowed for much more effective communication around new features.

In other areas being seperate from the rest of the system allowed for experimentation. A different language was introduced in one context that was much more suited to the problem of comparison.

The front end code base was often under high load which previously this meant scaling the entire system. Using this modular approach meant the concerns of each system could be addressed in isolation. This lead to a more performant application at a fraction of the cost.

Implications of Modular System

In introducing different contexts, each with their own abstractions, the obvious implication is that some translation is required when they communicate. Its important to note that this mapping has always existed but was either applied mentally, or worse, in the leaked into UI. The ability to contain this mapping programatically at the boundaries of the applications means frees you from making compromises in other areas.

Splitting an application into distinct modules may increases the amount coordination required to deploy the application. However this is easily surmountable using tools like capistrano, upstart, nginx, AWS etc.

Conclusion

Solving any problem effectively means acknowledging that it can be solved in a number of different ways and finding the right approach. Delivering an application means indentifying each of the distinct problems that exist and finding or evolving abstractions for each. With the right abstractions in place one can easily solve problems within this domain to quickly deliver features and produce a more effective and performant application.

blog comments powered by Disqus

About

A blog by Michael Jones, a developer currently working at forward, whose interests include: web technologies; ruby; functional programming; design and making the perfect cup of filter coffee.

Subscribe by RSS

Recently…