Chapter 4. Designing for Rapidity

Designing an enterprise-level system is a difficult and challenging task. Business requirements are typically both complex and diverse, while concerns such as security, integration, operational management, scalability, performance, internationalization, and standards compliance all contribute to making the job of the architect a hard one.

Consequently, any architecture that meets all of the expectations that surround enterprise-level software is an achievement. Unfortunately, where rapid development is concerned, meeting all requirements is only part of the story. For a RAD project, the architect must also design for timeliness of delivery.

This chapter looks at the importance of design for rapid development and examines how decisions made during the definition of a system’s architecture can have a significant impact on the ability to complete systems in a reduced timeframe.

Guidelines for design decisions that help to decrease the development timeframe of a project are explored, and we take an impartial view of the merits of the J2EE platform and outline practical J2EE-based architectures that are complementary to rapid development.

Architecture and Design Objectives

Before delving into the details of architectures that can help with rapid development, let’s establish exactly what is meant by the terms architecture and design.

Asking this question often stirs up considerable debate among IT professionals, especially architects. Many working hours have been lost as software engineers split hairs over the differences between the roles of the architect and designer. In the interests of productivity, and for the purposes of this discussion, architecture can thought of as the big picture.

It’s helpful to think of architecture as the blueprint for building the system. In this way, architecture forms an overarching set of principles, standards, protocols, frameworks, and directives that orchestrate the various design elements of the overall application.

Architecture differs from design in terms of scope. Architecture takes the wider view; design has a much narrower focus. Put simply, architecture has breadth and design has depth. The architect paints with a wide brush; the designer is more focused and concentrates on adding detail to the picture.

Architecture is important for rapid development because it forms the basis for large-scale software reuse. Considering the wider picture enables the identification of readily reusable components for quickly assembling a system. System architectures are themselves a reusable commodity, as architectures tailored to specific problem domains can form part of a company’s adaptive foundation.

The following list is not exhaustive but identifies those system traits the architecture would typically include:

  • Performance.

    For a given load, will the application meet the performance levels detailed by the system’s technical requirements? For example, will the system provide a two-second response time for a specific user action under maximum load?

  • Scalability.

    Can the capacity of the system to undertake higher loads be increased without the need to change the code?

  • Robustness.

    Is the system fault-tolerant and able to comply with the levels of availability defined in the customer’s technical requirements?

  • Maintainability.

    Is it easy for developers to correct any defects that surface or to add new functionality?

  • Manageability.

  • How easy is it for IT operators to manage the operational needs of the system in a production environment?

  • Security.

    Given the nature of the information maintained by the system, is the level of security sufficient?

  • Customer standards.

    Does the system conform to the customer’s own architectural standards and principles?

  • Timeliness.

    Can the solution be delivered in the timeframe available?

The timeframe available in which to deliver the system is an important factor when defining the system’s architecture and warrants equal consideration with other architectural concerns, such as system performance, scalability, robustness, security, and maintainability.

The priority timeliness is given should be set by the customers, and that priority must be respected by architect and designer alike. A common failing in the IT industry is to overengineer solutions, thereby risking missed delivery dates. The time to develop a solution can be of paramount importance to the customer. A drop-dead date for a system could be just that—any later and the customer might be out of business.

warning

Though the architect must consider all customer requirements, the architect also has a responsibility to advise the customer on the potential implications for the system if architectural compromises prove necessary to achieve a short-term delivery date.

RAD Architecture and Design

A RAD architecture is one that upholds timeliness of delivery as a central overarching principle. A RAD architecture both meets the needs of your customers and expedites the delivery of the system. In short, it is about defining an architecture that is implemented easily and efficiently by the development team and hence completed in a timeframe acceptable to the customer.

The following are some options for achieving this goal.

Work to the Strengths of the Team

Contrary to what many project managers would like to believe, software engineers are not faceless drones who spend all their spare time reading technical reference material on Java. The members of any given project team have acquired a diverse and varied range of skills and knowledge during their time in the software development industry. Knowing the strengths of the team is an important consideration when designing the system: the architecture should take advantage of these strengths.

Some people may find this a contentious statement, arguing that architecture must be based on the technology that best fits the system requirements. This is a valid argument. If the choice of technology was always based on the knowledge of the team, most IT applications would still be implemented in COBOL. Nevertheless, the team’s collective technical background should be taken into consideration when time is a critical factor for the project.

important

The same rule applies when looking to the job market for skills for the project. If members of the team leave, or a new project team has to be formed, filling vacant job roles could prove problematic if skills in an uncommon technology are required.

As an example, consider the choice of which scripting language to adopt on a new project. Both Jython and Groovy are excellent choices as scripting languages. Both are each compatible with Java and offer highly expressive language constructs. Nevertheless, if the team has a large base of experience with Perl, then neither Jython nor Groovy may be the best option. Where possible, you may wish to stick to what the team knows well and work to your strengths. Without doubt, there is value in learning new technologies—just be cautious of learning on time-critical projects.

Note

Chapter 9 looks at Jython and Groovy.

Use Best-of-Breed Frameworks

J2EE is all about frameworks, yet extensive as the J2EE platform is, it does not cater to every scenario. Enter the application framework to plug the gaps.

A suitable software framework can significantly reduce the amount of code we need to write and improve the quality of the design. Thanks to the groundswell of support for all things Java, J2EE developers are spoiled for choice when it comes to the availability of software frameworks.

Frameworks are available for just about every conceivable application, including Web development, security, build environments, test environments, and GUI development. Table 4-1 lists a selection of the popular offerings from the Java community and highlights the diversity of the frameworks available.

Table 4-1. Open Source Frameworks

Name

Description

Reference

Web

Apache Struts

Struts is a popular Web application framework based on the model-view-controller (MVC) design paradigm.

http://jakarta.apache.org/struts/index.html

JavaServer Faces

A technology for developing user interfaces for Web applications using an industry-standard set of APIs. JavaServer Faces technology was developed under the Java Community Process as JSR-127.

http://java.sun.com/j2ee/javaserverfaces

Apache Tapestry

Dynamic Web application development framework that centers on a component model in preference to a scripting approach.

http://jakarta.apache.org/tapestry

Persistence

Powerful, high-performance, object/relational persistence framework for Java objects.

http://www.hibernate.org

Hibernate

ObjectRelational-Bridge

OBJ is an object/relational mapping tool from Apache Software Foundation.

http://db.apache.org/ojb

Logging

Apache Log4J

Log4J provides an externally configurable logging framework.

http://logging.apache.org/log4j/docs/index.html

Application

Spring

Spring provides a layered framework for producing Java and J2EE applications. The Spring framework can either augment the services of the J2EE platform or be used standalone for developing highly flexible component-based systems.

http://www.springframework.org

Build Environment

Apache Ant

Popular Java-based build utility that uses an XML-based syntax for creating build scripts.

http://ant.apache.org

CruiseControl

Framework for supporting the practice of a continuous-integration build environment.

http://cruisecontrol.sourceforge.net/index.html

Apache Maven

Maven describes itself as a project management and project comprehension tool, and takes responsibility for managing and orchestrating the various artifacts that the project comprises.

http://maven.apache.org

Testing

JUnit

An almost ubiquitous xUnit-based testing framework for Java developers.

http://junit.sourceforge.net

Apache Cactus

Cactus extends JUnit and focuses on the challenges of testing server-side components.

http://jakarta.apache.org/cactus/index.html

The choice of which framework to use on a project is an important decision that can be critical to the success of the project. The following guidelines should help you through the decision-making process:

  • Team knowledge.

    Look to work with products that have proven successful on other projects, and consider frameworks with which the team already has experience.

  • Maturity.

    How long has the framework been used in commercial software development? Is it an open source project maintained by Apache, or is it the result of someone’s PhD? A mature software product has all the rough edges knocked off. Be prepared for problems if the software has yet to be used on a commercial development.

  • Fit for purpose.

    Pick a framework that meets the specific needs of the system. Where an acceptable match cannot be found, longer term benefits maybe realized by investing in the development of your own framework for a specific business area.

  • Tool support.

    Tool support is a significant criterion in the selection of a suitable framework, because the combination of framework and development tool can offer significant productivity gains.

  • Longevity.

    Try to select frameworks that will remain supported for the predicted lifetime of the system. A framework that disappears from view once a system reaches production presents ongoing maintenance and support problems.

Picking the best framework for your project reduces the development timeframe and should contribute to the quality of the final application. Here are some metrics to use as a guide when assessing a framework in terms of its maturity and longevity:

  • Availability of mailing lists and discussion forums

  • Level of activity on these forums

  • Response time for users getting questions answered and quality of the answers

  • Frequency of new releases of the framework

  • Community feedback in terms of perceived quality and usability

  • Number of open defects

  • Quality of documentation

Choosing a framework is an important decision, so be sure to do your homework before making it.

Think Ahead

By thinking ahead during the design process, you can make the lives of developers and testers alike a lot easier. The need to think ahead is especially relevant in the area of testing.

Sadly, testing is often unfairly seen as the poor relation to development. This perception is unwarranted, as testing is an integral part of the software development lifecycle. As a rough rule of thumb, testing hours on a project will likely equal that of development—any less, and the system is probably undertested.

Any actions you can take as an architect to make testing easier will not only result in a higher quality system, but also reduce the total testing effort. Exactly how the architect can design with testing in mind is largely dependent on the application.

Note

Testing is a key part of RAD and is covered in some detail in Chapters 14 and 15.

Outlined below are some points for consideration when working on the system design.

Include Test Stubs and Unit Tests as Part of the Design

Considerable effort is often wasted on projects when developers produce their own ad hoc testing harnesses. These test harnesses sprout like weeds throughout the project’s code base. They burst into existence as developers find themselves unable to test new components or modules due to a dependency on a piece of software that hasn’t yet been written. The solution is often to quickly implement a test stub in place of the missing software. Unfortunately, this task consumes time, and on a large project, this practice can quickly get out of hand with engineers duplicating the same test stubs.

These test stubs seldom find their way into source control and are often of dubious quality because no time has been set aside to the developer for this task on the project plan. To avoid this problem, the architect must think strategically and incorporate all necessary test stubs into the design. The designer must work in conjunction with the project manager to ensure software components are built in a logical sequence to avoid the need for ad hoc test stubs. Where this is not possible, the test stub should be included as part of the object model and time set aside on the plan for the development of the stub.

With this approach, all test harnesses and stubs become part of the project’s build cycle and are available to the entire team. They can also be made available to the test team for early testing of delivered components. The test team in turn can give feedback on the quality of the test stubs.

Therefore, the rule is, think about your testing needs early on, and design testing into the system. Your developers and your testers will thank you for it.

Be Wary of Weakly Typed Interfaces

Due to the surge in popularity of XML as a format for data transfer, the interfaces on distributed components the world over are taking on a very similar appearance. Thus, many methods now have the signature

void update(String document);

The parameter document in this case carries an XML document as the payload. Using XML in this way has largely taken over from the conventional approach of defining each argument as a typed parameter on the method as it provides an effective approach for ensuring loose coupling between components. The structure of the XML document can be modified without the need to change the interface, allowing for a design that is very resilient to change.

The downside to this approach is that the compiler cannot perform any type checking of the data on our behalf. Type checking, or validation of the document against a schema, must now take place at runtime. With XML, Java becomes a type-less language.

This presents the test team with something of a headache. Errors are harder to catch at runtime than at compile time, and the testing effort must now be exhaustive.

If you intend to use XML in this manner to achieve a highly decoupled system, then ensure the necessary safeguards are in place to replace the compiler’s type safety. Where practical, validate against an XML schema and always log any errors caught at runtime in a manner that makes them immediately apparent.

Be Wary of Designing for Reuse

Software reuse is a key element of RAD, but reuse is a double-edged sword. Making the reuse of software components part of your strategy for rapid development is not an easy task, even with the benefits of Java as an OOP language and J2EE as a container for housing prefabricated components.

Designing code for reuse is a difficult and time-consuming task. The developer must consider all reasonable future scenarios for a component and accommodate them accordingly within the software architecture. This adds excessive complexity to the component’s design and implementation. On a project with a tight timeframe, you should avoid overengineering the architecture for future reuse purposes. Rapid development project teams should design only to have the software meet the current known requirements, not possible future ones.

This fits in with the fundamental XP principle of keeping the design simple—a principle aptly described by the XP expression, “You ain’t gonna need it,” or YAGNI. Remember, less code means fewer defects.

Note

Chapter 3 covers the practices of XP.

warning

Rapid development requires designing with reuse as opposed to designing for reuse.

Designing for reuse adds considerable complexity to the project and hence consumes additional resources and time. Instead, develop reusable components as a separate research and development project aimed at building your company’s adaptive development foundation.

Have the components built, tested, and documented before rapid development project teams use them. A suite of suitably designed prefabricated components provides a development company with a competitive advantage, but this is an investment that needs to be made as part of a company’s preparation effort for rapid development. Don’t build the components of your adaptive foundation on time-critical customer projects.

Apply Orthogonal Design

Orthogonality is a term taken from geometry that applies to two lines that intersect at right angles. As vectors, these lines are said to be orthogonal and therefore independent. Thus, move along one of the lines, and your position projected on the other line remains unchanged.

Despite its origins in geometry, orthogonality is becoming a watchword in development circles after being popularized as a software engineering concept by Andrew Hunt and David Thomas in their book The Pragmatic Programmer [Hunt, 1999].

For software engineering, the term neatly summarizes those elements of design that are synonymous with well-constructed software; that is, they exhibit the admirable characteristics of loose coupling and high cohesion.

Orthogonal components are independent, self-contained, and have a clearly stated responsibility. Changes to the internals of an orthogonal component do not impact other components within the system.

This independence between components is vital to rapid development. A loosely coupled system can more easily accommodate change than one that is tightly coupled. A change in the database should not require a change in the code responsible for the application’s user interface, and vice versa.

An orthogonal design safeguards against change, and a design that can absorb the impact of change supports rapid development. Moreover, components that exhibit orthogonality are easier to code, test, and maintain due to their self-contained nature. Again, these are all attributes that help make a team productive and greatly benefit a RAD project.

Object-oriented development practices, if applied correctly, result in orthogonal designs. A staple of object-oriented design is the use of interfaces. By designing our components around interfaces, we can use interfaces as shields against implementation changes.

One successful approach for achieving an orthogonal design is to base your design upon the use of layers.

Adopt a Layered Architecture

In software engineering, separating the application code into layers is good design practice and enables the construction of robust applications. In the layered approach, each layer provides its services to the layer immediately above through a series of well-defined interfaces.

Each layer should rely only on the services of the layer immediately beneath it. This ensures higher layers in the design hierarchy are insulated from changes in the lower layers.

Here are the layers commonly found in business software:

  • Presentation, for user-interface components

  • Application, where the system’s business logic resides

  • Data, for providing business components with access to external resources such as persistent data stores or other enterprise systems

Layers are abstract and represent a logical boundary within the software, not a physical one. Distributed computing makes it possible to achieve a physical separation of a system’s logical layers into tiers. The tiers of a multitier architecture are physically remote from one another, typically located on different servers or executing in a separate process. This physical division of the layers into tiers gives rise to a multitier architecture.

The architecture of a J2EE application comprises three tiers.

Client Tier

The client tier is the “final front-tier.” It should contain code specific only to the user interface and rely upon the middle tier for all business knowledge. J2EE clients run on anything from desktops to handheld devices and access the middle tier remotely, using an appropriate network infrastructure such as a LAN or via the Internet.

Middle/Business Tier

On the J2EE platform, this middle tier is the J2EE server, which provides services for addressing the concerns of both Web clients and application business logic. The middle tier of the J2EE platform divides into two tiers for each of these concerns:

  • The Web tier provides services for orchestrating the interaction between Web-based clients and the system’s business logic

  • The EJB tier manages the system’s business components deployed within the tier as Enterprise JavaBeans

The J2EE server manages access to resources required by the business components. These resources reside within the enterprise information system (EIS) tier.

Enterprise Information System Tier

The EIS tier, or back-end tier, is the lowermost tier in J2EE and houses the enterprise resources upon which a J2EE application relies. Examples of enterprise resources include relational databases and existing legacy systems. The resources of the EIS tier do not fall under the control of the J2EE server. Instead, the J2EE server offers a set of standard services for integrating with EIS tier resources. These services include

  • The JDBC API for relational database access

  • Java Naming and Directory Interface (JNDI) for access to directory servers

  • The J2EE Connector Architecture (JCA) for integration with existing information systems

  • Java Messaging Service (JMS) for asynchronous communications with other application resources

The J2EE server undertakes the responsibility of managing and pooling connections to the various resources of the EIS tier as well as the management of transactions that span enterprise resources.

The J2EE platform makes possible the design of orthogonal systems using multitier architectures. The next section considers the strengths and weaknesses of using multiple tiers and explores some design options for J2EE architectures.

Approaches to J2EE Architecture

Building applications with multitier architectures involves the use of distributed computing, a complex area of software engineering. Although the J2EE platform does much to simplify the development of three-tier systems, distributed computing makes designs of this nature inherently more complex than systems comprised of only one or two tiers. This added complexity means architects should carefully consider whether the requirements of the system justify a distributed solution.

In the past, the prominence of Enterprise JavaBeans (EJBs), the flagship technology of the J2EE platform, has lead some architects to produce complex multitier architectures for systems whose requirements would have been met with a simpler model.

Although EJB technology has possibly the highest profile of all the J2EE services, Enterprise JavaBeans form only part of the J2EE specification, and their use is not mandatory in all J2EE applications. Furthermore, EJB architectures are possible that do not rely on the remote-method invocation services of the J2EE container.

If you can avoid the use of a distributed architecture for systems that do not require it, then simpler designs result. Avoiding complexity in this way is important for rapid development projects, as complex designs can wreak havoc on tight schedules.

Complexity makes:

  • Designs and code difficult to understand

  • Timeframes longer due to steep learning curves

  • Software maintenance expensive

  • Changes difficult to assess and apply

  • Mistakes easy to make

Over the course of the next sections, we examine the strengths and weaknesses of distributed systems and consider possible architectures that do not rely upon the services of distributed components.

Two-Tier Versus Multitier Architectures

Prior to the emergence of distributed computing technologies like J2EE and CORBA, enterprise information systems typically adopted a two-tier, or client/server architecture. Under the client/server model, business logic was implemented as an integral part of a rich, or fat, client application that accessed data housed in a central database server.

The client/server model enjoyed considerable success. It was easy to understand, simple to test, and well supported by numerous high-level visual development tools. These benefits made the client/server architecture ideal for rapid development. Sadly, two-tier architectures do have some key shortcomings:

  • Deployment.

    The client/server model sees all functionality built as a monolithic, standalone application and deployed directly to the client machine. Large organizations requiring widespread deployments to thousands of desktops face a huge, logistical challenge. Often, deployments of this nature can only be made out of hours, or in some extreme cases, over the course of a weekend. Consequently, while the two-tier model enables software changes to be applied quickly, it has inherent weaknesses when it comes to releasing new, business-critical, functionality in a timely manner.

  • Sharing of services.

    The monolithic structure of client/server applications presents some very difficult problems when it comes to sharing services between applications across the enterprise. Increasingly, organizations require their core IT systems to share information or services. The lack of a centralized business or services tier in the client/server model makes this all but impossible. One option is to share common code modules, or components, between systems. However, this again presents deployment problems.

  • Poor separation of presentation and business logic.

    Placing business and presentation logic within the same tier can lead to a commingling of these two concerns. Arguably, this is an indication of poor design rather than a true weakness of a client/server architecture. A well-designed application would see the two layers cleanly decoupled using well-founded object-oriented design practices. Unfortunately, often over the course of the lifetime of the system, software entropy sees the two layers merge without the benefit of physically separate tiers to keep the layers logically distinct.

In searching for a solution to the shortcomings in the client/server model, developers concluded that you can never have too much of a good thing. If splitting systems into two physical tiers proved effective, then the addition of a further tier would also be a good thing. Experience proved them mostly right, and so the middle tier was born.

The use of a middle business tier offers many benefits beyond that of the client server model:

  • Centralized business intelligence.

    The middle tier enables business components to be located in a central location and accessed remotely by disparate systems. Here, the middle tier addresses the problem of core business functionality being effectively locked away within the heavyweight clients of the client/server model.

  • Ease of deployment.

    Thanks to the middle tier, gone is the headache of deploying business components to thousands of desktop machines over the corporate network. With all business components centrally located, changes can be deployed quickly and easily with minimal impact on the system’s user base.

  • Redundancy.

    The deployment of business components into a clustered middle tier provides built-in redundancy for systems. Thus, the use of multiple machines to replicate the middle tier sees important business components maintained in a high-availability environment.

  • Scalability.

    Clustering components of the middle tier over multiple server nodes also allows for scalability. As system usage increases, so too can the number of machines in the architecture.

These benefits have pushed the multitier model to the forefront as the architecture of choice for enterprise software. The technology of the J2EE platform provides a distributed component model for developing systems with multitier architectures, with Enterprise JavaBeans a key technology for developing business components for deployment to the middle tier.

Enterprise JavaBeans

Enterprise JavaBeans helps realize the RAD concept of developing software using component-based architectures. The EJB architecture defines components that encapsulate functionality into deployable modules with clearly defined interfaces known as enterprise beans.

The J2EE platform supports three types of enterprise bean objects:

  • Session beans, for encapsulating business logic

  • Entity beans, as a mechanism for presenting an object-based view of entities within a persistent data store

  • Message-driven beans, for consuming messages delivered to the server via the JMS API

The remainder of this discussion focuses on the use of session beans for building distributed enterprise solutions.

An EJB architecture provides substantial benefits when building an enterprise system:

  • Improves productivity by managing transaction and security concerns on behalf of the developer based on information specified in the enterprise bean’s deployment descriptor

  • Provides scalability by intelligently managing the allocation of resources and transparent state management of stateful components

  • Supports and mediates access to business components by multiple client types, including fat clients and thin browser-based Web applications

  • Insulates the client from networking issues by providing location transparency, whereby methods on a component can be invoked regardless of the physical location of the client or the component

The final point concerning location transparency is of specific interest, as this EJB technology makes possible the development of distributed architectures with the J2EE component model.

Remote and Local Client Views

Clients of session bean objects can view methods on a bean’s interface either remotely or locally. With a remote method invocation, a client running in a separately executing Java Virtual Machine (JVM) accesses the bean instance. With local access, the client resides within the same JVM as the bean object. Alternatively, as of the EJB 2.1 specification, clients may access session bean objects through a bean’s Web Service. This is essentially another form of remote access.

Clients obtain remote access to a session bean object through the enterprise bean’s remote interface and remote home interface. These interfaces ultimately extend the java.rmi.Remote interface, part of standard Java Remote Method Invocation (RMI). By building on RMI, clients can access bean functionality across machines and process boundaries. The complexities surrounding network connection issues are all handled by RMI on behalf of the developer.

An enterprise bean may also expose a local interface. Local interfaces do not offer location transparency, meaning that clients can access a bean only if it is executing in the same JVM through its local interface.

We look at the benefits of local interfaces shortly, but first let’s consider some of the implications surrounding the development of enterprise beans that expose remote interfaces to their clients.

Distributed Components

Providing enterprise beans with remote interfaces makes possible the development of systems with distributed architectures. The J2EE platform does a very creditable job of reducing the difficulties associated with building complex architectures of this type. Nevertheless, adding remote interfaces to business components results in certain issues of which the architect must be aware before deciding upon a distributed solution.

Rod Johnson eloquently raised awareness of these issues within the Java community in his book Expert One-on-One J2EE Design and Programming [Johnson, 2002]. These issues include performance overheads, complexity, and object-oriented impedance.

Performance

EJB technology relies on standard Java RMI for its distributed capabilities, which provides the client of the enterprise bean with location transparency when accessing the bean through its remote interface. However, the process involved in making a method invocation across a JVM or machine boundary, makes RMI calls several orders of magnitude more expensive than the equivalent calls on a local object.

To understand why remote calls incur a performance penalty, let’s review the steps involved in an RMI method call.

In making the call, RMI creates a stub object on the client responsible for sending method parameters and receiving return values across the network. This packaging of method calls for transmission across the wire is called marshaling and is a common approach to remote method calls. RMI uses Java object serialization to convert parameters into a stream of bytes for sending across a network.

The client stub does not talk directly to the object on the server but to an RMI skeleton object, which is the server-side equivalent of the client stub, and retrieves all calls sent across the network before forwarding them to the real object. This server-side skeleton object makes the call on the remote object on the client’s behalf.

This same process is followed every time a method on a session bean object’s remote interface is called. The overheads of remote method calls means careful design is necessary to ensure the benefits of location transparency do not come at the cost of system performance.

tip

Passing large objects as parameters in remote method calls further reduces performance. If an object contains references to other objects, then these member objects are also serialized as part of the call. To reduce the size of some objects, you can exclude unnecessary objects by marking them with the transient keyword.

Since RMI uses Java serialization for marshaling, for very large objects you should consider overriding writeObject() on java.lang.Serializable and implementing some form of compression algorithm. Don’t forget to implement a corresponding decompression algorithm for readObject().

Complexity

Although RMI can make the calling of remote enterprise beans a seamless process, distributed computing still introduces additional complexities for the developer.

The first issue is the need for developers using RMI technology to be aware of the subtle but important differences between making remote calls and calling a local Java object.

Parameters in a remote call are passed by value, due to the need for RMI to copy the parameter in order to send it across the network. If the remote object modifies the state of an object passed to it by value, then the object referenced by the caller does not reflect these changes.

With a call on a local object, parameters are passed by reference. Consequently, if the called method modifies the state of an object passed as a parameter, then the object referenced by the caller does reflect these changes. This is a subtle difference in the semantics of the call, but it can cause frustration for developers unaware that Java’s calling conventions have suddenly changed.

Remote clients must also guard against the prospect that networking issues could cause a remote call to fail. Trapping these network errors isn’t an onerous task, as you only need to catch thrown java.rmi.RemoteException exceptions. However, complexities arise with the need to handle these exceptions, because they are orthogonal to the standard exceptions a business method would normally throw. The architect must have a strategy in place for satisfactorily handling these remote exceptions and translating them into meaningful error messages for the presentation layer. Avoiding the need to deal with remote exceptions therefore has positive implications for rapid development.

Object-Oriented Impedance

Employing the distributed capabilities of EJB technology requires a change in the way we apply object-oriented design practices.

In a nondistributed architecture, developers work directly with objects from the domain model. Here, domain objects represent business entities and expose interfaces with fine-grained methods, such as get and set methods.

The performance overheads of a distributed application preclude the use of objects with fine-grained methods, as they increase the number of network roundtrips when a client interacts with a remote object.

To keep the number of roundtrips to a minimum, remote interfaces have to be coarse-grained, whereby methods combine the functionality of several fine-grained methods into a single call.

To provide guidance when building distributed architectures, Sun Microsystems published a collection of J2EE design patterns [Deepak, 2003]. These patterns are essential reading for anyone building distributed applications. The Session Façade and Transfer Object patterns both offer effective strategies for managing the performance overheads of remote calls.

Despite the existence of these patterns, they still impose on the architect’s object-oriented thinking process. The architect must be aware of the constraints of the implementing technology and reflect this in all designs. Having the implementing technology dictate the makeup of a component’s interface is counter to the practice of good object-oriented design.

Choosing the Appropriate Design

Distributed components are not a prerequisite for all enterprise architectures. Unless the requirements of the system dictate a distributed architecture, a simpler application is possible by avoiding the need to employ the remote method invocation capabilities of the J2EE platform. The architectural decision as to whether to use distributed components therefore has a significant impact on rapid development projects.

In this section, we look at two possible J2EE architectures, Web-centric and EJB-centric, although numerous permutations of the two designs presented are possible. The next two sections evaluate the merits of each design from the standpoint of a system whose main architectural driver is the delivery timeframe.

Web-Centric Architecture

One trend in enterprise systems that has become very apparent is the move away from fat user interfaces to lightweight clients. In fact, thanks to the popularity of the browser, user interfaces have become positively anorexic.

The Web application is emerging as the de facto standard for enterprise applications, a trend that has the potential to make the task of developing enterprise software far simpler.

Let’s consider a basic J2EE architecture to support an enterprise Web application that does not make use of the services of EJB technology. Figure 4-1 illustrates a Web-centric design.

Web-centric enterprise architecture.

Figure 4-1. Web-centric enterprise architecture.

Does anything in Figure 4-1 look familiar? The presentation tier is gone. In its place is the Web container (or Web tier), acting as a consolidated presentation and business tier. The browser-based interface enables us to return to a model similar in structure to the classic client/server architecture. Thus, the Web-centric model sees a single JVM housing presentation and business logic in a common Web tier. This design swaps a multitude of client machines for a single server and removes the deployment problems associated with the client/server model.

The Web-centric design shown has just two tiers (or two-and-a-half with the browser): the Web tier, and the EIS tier. The use of layers within the Web tier separates user interface code from business logic.

note

For clarity, the architecture depicted in Figure 4-1 does not show all of the layers required in an enterprise system. For example, a production system would put in place layers to split business objects from lower-level data access objects.

Business objects are implemented as Plain Old Java Objects, or POJOs, in preference to heavier weight enterprise beans. The term POJO refers to a vanilla Java class that exposes no specialized interfaces.

The nondistributed Web-centric architecture shown in Figure 4-1 has several key benefits:

  • User-interface and business components both execute within the same JVM, providing high performance

  • Using plain Java objects instead of enterprise beans for encapsulating business logic removes the additional configuration and deployment complexities associated with EJB technology

  • Unit testing of the business objects is simpler because, unlike session bean objects, they can be tested without being deployed to the J2EE server

  • No RMI calls are involved, so standard Java pass-by-reference calling conventions remain unchanged

  • Business objects can expose fine-grained interfaces

A prerequisite for most enterprise architectures is scalability. The Web-centric design scales across application servers, although suitable load-balancing hardware or software is required to manage the allocation of requests between server nodes.

Like any solution, the Web-centric architecture has its limitations, including the following:

  • Supporting Web-interfaces only.

    The Web-centric architecture supports only browser-based interfaces. If the system requirements mandate fat clients accessing the business layer remotely, then an EJB architecture is more appropriate. Alternatively, the use of a framework such as Apache’s Axis enables business object interfaces to be exposed to heterogeneous remote clients as Web Services.

  • Security.

    While it is possible to secure Web applications, plain Java business objects do not offer the same declarative security model as is available for enterprise beans. Securing business objects within the Web-centric architecture to the same method level allowed by EJB technology requires the time-consuming task of writing application-specific security code.

  • Transaction support.

    To support transactions, business objects must implement their own transaction management. The transaction management services of the J2EE server can be invoked programmatically, but this approach involves considerably more effort than the declarative transaction model EJB technology provides.

  • Threading issues.

    The EJB container manages concurrent threads of execution on behalf of the developer. In the Web-centric model shown, the developer is responsible for addressing threading issues. The development of multithreaded systems is notoriously error prone and time consuming.

The next section introduces enterprise beans that expose local interfaces into the Web-centric architecture.

EJB-Centric Architecture

To counter some of the shortcomings in the Web-centric architecture presented previously, this next solution modifies the architecture to incorporate enterprise beans that provide a local view of their interfaces to clients.

Figure 4-2 illustrates a possible EJB-centric architecture for our Web application.

EJB-centric enterprise architecture.

Figure 4-2. EJB-centric enterprise architecture.

The architecture shown Figure 4-2 turns the business objects into session beans and deploys them into the J2EE server’s EJB container. The session bean objects that represent the system’s business components do not expose remote interfaces. Instead, they each make available a local interface to clients.

The EJB 2.0 specification introduced local interfaces for enterprise beans in response to industry concerns surrounding performance issues with EJB architectures. Placing a local interface on an enterprise bean enables a client resident within the same JVM to invoke the methods on the bean instance using standard Java pass-by-reference calling conventions, avoiding the overheads incurred in a remote call.

In the architecture shown in Figure 4-2, both the J2EE server’s Web container and EJB container exist within the same JVM, making the components of the Web application local client’s of the session bean objects. The EJB-centric architecture still has the same two-and-a-half tiers as the Web-centric version, with the Web container and EJB container acting as layers within the middle tier.

The benefits of an enterprise architecture built using enterprise beans with local interfaces include the following:

  • Performance is excellent as in-process method invocation does not carry the overheads of Java RMI

  • The declarative model of EJB technology is available for addressing security, transaction management, and thread safety

  • Remote exceptions are not thrown and so do not have to be handled

  • Enterprise beans can expose fine-grained business interfaces

The EJB-centric architecture presented represents a valid use of EJB technology where the system requirements call for declarative security and transaction management. Some of the weaknesses of this architecture include:

  • Supports only Web interfaces.

    Despite the introduction of EJB technology, the architecture supports only Web clients colocated within the same JVM.

  • Physical separation of layers.

    It is not possible to split Web components and business components into separate tiers for deployment to different servers.

  • Configuration overheads.

    The EJB architecture introduces configuration overheads for the business components. You should ensure these overheads warrant the benefits EJB technology brings to the architecture.

  • Container-bound testing.

    As enterprise beans, the business components now require the services of the EJB container for unit testing purposes.

The architecture examples discussed are just two possible approaches to building enterprise systems. Simple architectures are faster and easier to implement than complex ones. Distributed architectures introduce complexity. Ensure the requirements of the system justify this additional level of complexity before embracing a distributed component model.

Summary

Designing for rapidity requires making decisions during the definition of a system’s architecture. These decisions impact the ability of the project team to deliver an IT solution within the customer’s timeframe.

Some of the factors we discussed in this chapter when timeliness of delivery was a key concern of the customer included the following:

  • Delivery timeframe is a contributing factor to the definition of the system’s architecture

  • Build software that is orthogonal using a layered architecture

  • Design the system for testing from the outset

  • Work to the strengths of the team

  • Adopt proven frameworks

  • Keep designs simple and be wary of designing for future reuse

  • Avoid building of unnecessarily complex system architectures by using only the J2EE technologies that help solve the business problem

In the next chapter, we examine the benefits of modeling tools for rapid development and explore how the process of modeling can help the software engineer in the task of building enterprise applications.

Additional Information

The collection of J2EE patterns published by Sun Microsystems is available online as part of Sun’s Java BluePrints program, a developer resource aimed at defining a set of best practices for building Java applications. The Java BluePrints program, along with the core J2EE patterns, resides at http://java.sun.com/blueprints.

The two J2EE architectures presented in this chapter were based upon some of the architectures presented by Rod Johnson in his excellent book Expert One-on-One J2EE Design and Programming [Johnson, 2002]. This title offers guidance and advice for applying J2EE technologies to real-world business problems.

Patterns in architecture are becoming increasingly important. Martin Fowler’s book, Patterns of Enterprise Application Architecture [Fowler, 2002], introduces a range of patterns for building enterprise architectures.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.74.18