|
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Features Componentizing a Monolithic Application in Java
Using a simple homegrown component model and framework
Jan. 28, 2013 05:00 AM
Component-oriented development has many architectural advantages. In spite of this, many developers tend to solve problems the monolithic way on the first go. This article demonstrates how a monolithic design can be modified to achieve component-based design. During this conversion process, the necessity of Component Models and Frameworks are highlighted. The article demonstrates the componentization of an example monolithic application using a simple homegrown component model and framework developed by the authors. Introducing E-Store - A Business Application
Monolithic Implementation of E-Store
Figure 1: Class Diagram for E-Store Application The implementation of the above design in source code and binary code form can be obtained from the links provided at the end of the article. The implementation of the monolithic application is explained briefly in the sections below. Application Startup - UI public class UI { Listing 1: Startup Code - UI Class The E-Store class instantiates the Inventory and ShoppingCart classes during its startup as shown in Listing 2. public class Store {
Listing 2: Startup Code - E-Store Class
The inventory class initializes the stock during its instantiation, by creating instances of Product class objects. The code snippet is shown in Listing 3
public class Inventory {
} Listing 3: Startup Code - Inventory Initialization Once the startup is done, the UI class presents a console based menu as shown in Listing 4. Welcome to eStore!
1. Browse Catalog
Choose an option: Listing 4: Console based UI Menu When the user chooses any one of the options, the UI class calls upon its implementation in the E-Store business class. The implementation of each of these is explained briefly in next few sections. Browse Catalog Use case Realization public Collection<Product> getCatalog() {
return inventory.getProducts();
} Listing 5: E-Store Class - getCatalog() implementation Buy Items Use case Realization public boolean buyItem(String name, int quantity) { Listing 6: E-Store Class - buyItem() implementation Check Out Use Case Realization public double checkOut() {
} Listing 7: E-Store Class - checkOut() Implementation What's wrong with the Monolithic implementation? Table 1: Class dependency details
The tight coupling results in high resistance to change in implementation. For example, any change to Product class will require complete change in the application. Let us say that the E-Store likes to announce promotional sale for three days. During these three days, the total price of the shopping cart should be discounted by 10%. In order to achieve this, we need to change the ShoppingCart class implementation. When the ShoppingCart class is changed, the E-Store class also needs to be recompiled. When the E-Store class is recompiled, the UI class also needs to be recompiled. What happens at the end of the promotional sale when the E-Store wants to discontinue the discounts? We need to recompile all the 3 classes one more time. Ideally, since the changes affect only the ShoppingCart behavior, rest of the application modules should not have been affected. But due to the tight coupling, other modules are also affected. Loosening the Coupling through Componentization In order to make the application modules loosely coupled, we need to componentize the application. A component is a deployable piece of software that would be independently developed and independently maintained. Independence here refers to development and maintenance of a component independent of the other components which collaborate with this component in an application assembly. In a component based application, change to one component should not directly affect the application. We avoid tight coupling between components by introducing the abstraction of Component Interface. A component interface exposes the signature of the functionalities implemented by component. The Component Interface will be a relatively stable entity as compared to the Component Implementation. A component consumes interfaces that it depends on for fulfilling the required functionality and provides interfaces for the functionality it provides. For collaboration with the other components, the component would work through the interfaces provided by the other components. Practically, the component should not depend on the implementation of the other components; it should depend only on the interfaces provided by those components. This way, the coupling among components is through the relatively stable interfaces and not through the highly instable implementations. Thus the principle of low coupling is upheld. In addition to the low coupling achieved, componentization of a monolithic application also brings about substitutability of components. This means a component of the application can be substituted by another component without affecting the overall application. The only requirement is that the replacing component must offer the same set of interfaces as was offered by the component being replaced. Componentizing the E-Store Application
Figure 2: E-Store Interfaces - Class Diagram In this E-Store application, there are four components represented by their interfaces - IProduct, IInventory, IShoppingCart and IStore. After the interfaces are extracted from the monolithic implementation, it will be a good design to get these interfaces packaged into a separate Java Package called estore.ifce. The package can also be compiled to a JAR resulting in a deployable and independently maintainable estore.ifce module. This module does not implement any component, but it simply defines ONLY the interfaces which would be implemented by other components in the application. All the components depend ONLY on this common interface module and they need not depend on individual implementation components. Following the above principle, if we separate the implementation of Product, Inventory, ShoppingCart, and Store into individual packages and into individual JARs, we get the package structure shown in Figure 3.
Figure 3: Package Diagram separating interface from implementation When we refactor the code into multiple components as shown above, two code segments fail to compile as shown in Figure 4 and Figure 5. Kindly look at Listings 2 and 3 for reference.
Figure 4: Compilation error in Inventory class post Componentization
Figure 5: Compilation error in Store class post Componentization The compilation errors occurred due to the fact that above code tried to invoke the implementation code of other components directly. We have arranged our dependencies such that one component would not depend on the internal implementation of the other component. The above code violates this. This problem can be solved in various ways. This is where all the component models and frameworks come to the rescue. Component models like RMI, EJB, Spring, OSGi and SCA have their own way of creating object references to components from the interfaces. Users can choose to use one of these frameworks or models for initializing the component. However in this article, we will look at a simple component model developed to solve this problem without using any of the component models and frameworks. This component model uses some of the principles of design pattern and best practices which is explained in detail below. The problem of direct reference to implementation can be resolved by introducing a ‘Factory' object that can be used by the component to obtain an object of the corresponding type. A generalized Factory object could have a signature as below: public interface IFactory<T> {
} To avoid tight coupling, the Factory object is really useful. So, instead of coupling to a concrete class which implements IProduct, the Inventory implementation can depend on a Factory object of type IFactory<IProduct>. By invoking the createInstance() method on the factory object, the Inventory class can obtain new IProduct objects. Similarly the IInventory and IShoppingCart objects can be obtained from the respective Factory objects using createInstance() method in the Store class. IProduct iPad = productFactory.createInstance(); To obtain a factory object, a FactoryRegistry class is used as a common factory registry for registering and retrieving factory objects using the whiteboard pattern. The common registry object can be implemented as shown in Listing 8. public class FactoryRegistry { Listing 8: FactoryRegistry Class The Inventory class can obtain a reference to a product factory object of type IFactory<IProduct> using the whiteboard pattern. Similarly any factory object can be retrieved from the FactoryRegistry. IFactory<IProduct> productFactory = One important question that remains unanswered is how, where and when these factory objects are registered with the FactoryRegistry. All component implementations only try to GET references. As mentioned earlier, component models like RMI, EJB, OSGi have their own service repository where these references are registered and components using these references look up the repository to get an object of the corresponding type. In this simple model, a registry program named ‘ComponentRunner' is handwritten which will look up for IFactory type interfaces and its implementations and register them appropriately so that getFactory method returns an initialized factory object. Kindly refer to the source code provided for details on ComponentRunner. Implementation of this model will help in resolving the compilation issue highlighted in Figures 4 and 5. The modified code without any compilation error using the factory pattern and registry lookup is shown in Listings 9 and 10. private void initStock() { Listing 9: Modified Inventory Class without compilation error public class Store implements IStore { Listing 10: Modified E-Store Class without compilation error Apart from the above highlighted modifications, the business logic implementation in the components remains the same as before in the monolithic case. Executing the Sample Application
Conclusion Reader Feedback: Page 1 of 1
Latest AJAXWorld RIA Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||