Engineering Effective Frameworks
Posted by indroneel on April 11, 2007
The rise in popularity of object-oriented frameworks is synonymous to the growing prevalence of Java and .NET platforms for enterprise application development. With mature frameworks numbering in the hundreds, it is only natural that there is a significant overlap in the target scenarios addressed by these solutions. While this poses a problem for application developers in choosing the right set of frameworks, it is equally challenging for framework providers to encapsulate the right mix of paradigms, concepts and features for their products to be widely accepted.
Over a period of time, and with the availability of proven design patterns and principles, framework development has become increasingly standards-driven. This has reduced technical maturity to being a qualifying factor rather than a differentiator for competing frameworks in the same technical space.
This article outlines some of the factors (apart from technical maturity) that should be considered while developing effective object-oriented frameworks for better acceptance.
The 40-60 Principle
Bespoke development should constitute at the most 40% of the total framework package offered. The rest 60% represent capabilities that can be realized using third-party components and libraries. Integration with other frameworks and platforms also fall under the latter category.
This separation of concerns help the development team to focus more on core framework capabilities. In avoiding duplicate development, the number of defects introduced in the overall product is also significantly reduced. The net effect of all the above is a framework that attains maturity in fewer iterations with shorter life cycles.
Freedom of Choice
A well-designed framework should incorporate an architecture that is open for extension. These extensions, in conjunction with the framework, are reusable across multiple application scenarios. Extensibility is provided through integration with third-party solutions at specific endpoints defined by the framework.
Freedom of choice entails the ability to integrate more than one product, as alternatives to each other, for the same set of features. As an example, consider the Struts framework that allows a choice of compiled pages (JSP), templates (Velocity and Freemarker) and components-based composition (JSF) as technologies for rendering Web pages. This freedom is usually limited by the flexibility (or lack thereof) at extension points for the corresponding framework.
Freedom of choice contributes towards the effectiveness of a framework in the following two ways:
- it increases the alignment of a framework to application-specific technology stacks resulting in a higher probability of adoption.
- it facilitates better technology stack definitions for applications with predefined frameworks as the base platform.
Integration of solutions from multiple providers rank high on complexity during application development. To counter this, providers should identify the most popular solutions available for specific functionalities and application layers relevant to their framework. Integration pieces for these solutions are then bundled as part of the standard framework distribution.
As an example, consider the Spring framework that derives a lot of its popularity through integration with third-party solutions like Quartz for scheduling, Hibernate and iBatis for data access, JOTM for distributed transaction management and Axis for web services to name a few.
Off-the-shelf integration can save a significant amount of effort that would otherwise have been spent on resolving issues due to version mismatch and in glue code development. For the target framework to be effective, this integration should highlight the following characteristics:
- Transparency: The integration should not require any special configuration on part of the solutions that are external to the framework.
- Simplicity: The integration should require a minimum additional configuration on part of the framework.
- Loose-coupling: It should be possible to ship the framework with only some of the integration pieces (plus associated libraries and resources) without breaking the overall framework capabilities. This is required since many applications will require only a subset of the integrated features provided out-of-the-box.
Good frameworks reduce programming complexity, effective frameworks reduce development time. One of the ways to achieve the latter is to avoid a full build and deploy cycle for every change made during application development. Hot deployment (also known as hot swapping) refers to the overall process in which implementation changes applied to a running application instance become effective without requiring a restart.
A good example of hot deployment is the ‘save-and-refresh’ syndrome while working with plain JSPs deployed within a servlet container. Changes made to a JSP are recompiled and redeployed immediately (or on the next page request) without an intervening server downtime.
While hot deployment may seem like a must-have feature for frameworks, it is not feasible to incorporate the same in many cases. This is because hot deployment is inherently error prone. The most common problems involve memory leaks that arise due to improper cleanup of stale codebases and dangling resources (open file handles, sockets and threads).
Additionally, hot deployment requires that the application continuously monitor itself for changes at pre-configured intervals or on specific events resulting in performance degradation. A framework that supports hot deployment must therefore provide the option to turn off this feature in a production environment.
A significant portion of an application’s codebase involves thin adapters that bind together modules from different layers. For example, presentation layer adapters provide the necessary logic that controls data transfer between the view layer elements (like form fields) and business layer entities (like EJB). These adapters and the entities/modules they interact with can be deployed on different frameworks.
Since the adapter logic is not resource intensive, it is possible to implement the same in a scripting language without significant overheads. These scripts are loaded and executed at runtime by the hosting framework using embedded, language-specific interpreters. Examples of such interpreters include Groovy, BeanShell and Janino to name a few.
Integration of scripting features with object-oriented frameworks has the following benefits and advantages:
- Since scripts are interpreted and not compiled, the corresponding sources are loaded for every invocation of a scripted functionality. This results in source-level changes to be immediately reflected (hot deployment).
- The adapter logic, in most cases, is procedural. Scripting languages provide a loose syntax that help bypass most of the rigidity of an object-oriented platform while retaining the advantages of encapsulation and inheritance.
Most object-oriented frameworks have their assembly information externalized into configuration files. Moreover, each framework follow its own mechanisms and semantics for accessing such information. This results in configuration becoming a non-trivial activity for enterprise applications that are built on top of multiple frameworks from different vendors.
For a framework to rate high on adoption, it is necessary to keep its configuration at a minimum and based on widely accepted mechanisms. The following list provide some pointers to achieve this.
- Provide the flexibility to define the same configuration in multiple semantics like properties information (name-value pairs), hierarchical XML data and Groovy scripts.
- Avoid forcing different configuration mechanisms for different aspects of the same framework (e.g. part file system and part RDBMS storage).
- Provide the option to define all configuration information in one file or split the same across multiple files.
- Distinguish between application assembly and runtime configuration. Internalize assembly information to the maximum extent possible. Use language-specific features like annotations to achieve this.
- Promote convention over configuration as popularized by Ruby on Rails.
Visual development environments greatly increase productivity of end-users in working with specific frameworks. With the availability of a wide variety of extensible IDEs (Eclipse, NetBeans and JDeveloper to name a few), creating a framework-specific visual environment amounts to providing a set of plugins for each of these platforms.
The following are some of the ways in which visual aids contribute to a framework-based development:
- Easy to comprehend user interfaces for configuration and assembly information.
- Automated code generation with placeholders for custom implementations.
- Syntax highlighting of source code and WYSIWYG editors.
- Debug mode application launching.
Creation of these visual aids should happen in parallel to the core framework development and released as additional distribution packages.
Model-driven architecture is a popular methodology when it comes to engineering complex enterprise applications. Frameworks that provide integration with model-driven software engineering tools will find preference in comprising the base platform for such applications.
MDA integration usually involves the ability to generate code, along with metadata and other externalized resources, that are deployed and execute within the scope of a target framework. The automated generation is based on meta models and domain specific languages as defined by the MDA process adopted.
In associating with a model-driven architecture, frameworks should provide support for available standards. Examples of such standards include the Rational Unified Process (RUP) for an end-to-end solution, Rose MDL and Eclipse EMF for meta modeling and Xtext for domain specific languages.