Number of calls versus separation of concerns
The theoretical base introduced in Part 1 applies to many implementation technologies. It does not matter whether the remote call invokes Enterprise JavaBeans (EJBs), web services, or JDBC providers. To keep the promise of being implementation technology-independent, I will concentrate on the structure of the remote calls, such as the input parameters, the number of calls needed to execute a use case, and so on. This is a major area where the application architect can improve the performance of a distributed application.
I will therefore investigate the structure of remote calls by looking at remote service interfaces and the (remote) components that provide them. In the sample use case of retrieving a shopping cart, you start with the EJB technology implementation and translate it into the new terminology: the Home interfaces translate to the remote Home component, providing the find services that return the objects found.
One important design guideline used in the design of components and interfaces in SOA (in fact, in any IT architecture) is separation of concerns [SoC1, SoC2]. This means that the components and services should have clearly defined, non-overlapping responsibilities.
In the example use case, this means that the ArticleHome component should not know about the CartHome
component and its Cart objects and vice versa. Instead, the ArticleHome component should only know about
article-specific data, and should use articleId as a key to retrieve articles.
To clarify this, look at Figure 1, which depicts the initial (Remote Entity Bean) implementation of the use case. It shows the four Home components used for the tables Cart, CartItem, Article, and ArticleDescriptions, with finder services that retrieve the objects using either the cartId or the articleId. Note that all calls starting from the client application are remote. For N items the client needs to perform 2+N*2 remote calls.
Figure 1: Initial implementation of reading the shopping cart

The diagram shows all calls to the different Home components needed to implement the example use case. You can see that the separation of concern rule is obeyed, as every component handles an isolated call to retrieve its related object. However, the number of remote calls is large, as each Article object and its description are retrieved separately.
After looking at the initial implementation, you can now investigate how to improve the performance by reducing the number of remote calls.
One common way to reduce the number of calls in such a setup is to design specific finder services in the ArticleHome
and ArticleDescriptionHome components to retrieve the articles in the shopping cart and their language-specific article data. Figure 2 shows the result.
Figure 2: Shopping cart specific services in the ArticleHome and ArticleDescriptionHome components

Note that the client only needs four remote calls for any number of items -- one to CartHome, one to CartItemHome, one to
ArticleHome, and one to ArticleDescriptionHome.
This design, however, violates the separation of concerns guideline because it requires that the ArticleHome and
ArticleHomeDescription implementations know about the shopping cart. They need to find out which articles actually are in the shopping
cart and then read the specific information for those articles. In a monolithic system, where shopping cart and articles are stored in the same database, this is easily implemented with an
SQL (or EJBQL) join between the Article and Cart tables. However, the following specific problems might arise from this approach:
- Assume you add auctions to the system using an
Auctiontable. In the auction, an article whose data is stored in theArticleandArticleDescriptiontables, is auctioned. Using the same approach, to read the auction similar to the sample use case of reading the shopping cart, you have to add services to theArticleHomeandArticleDescriptionHomeinterfaces to find articles for one or more auctions. So, theAuctionandArticlecomponents become dependent on each other. With each new subsystem added that needs finders in theArticlesubsystem, theArticlesubsystem becomes more complex. - Assume you want to use a different implementation of the
Articlecomponent. This could be required when the system grows and articles stored in different systems (like the Enterprise Resource Planning (ERP) system mentioned in the sample use case) need to be read and displayed in the use case. This new system does not know about the existing shopping cart, and thus, it is difficult to implement the finder services with thecartId(or theauctionId).
One solution that works well without increasing complexity too much is to change the finder services to accept not a single ID, but a list of IDs as input parameter. The finder services would then, in a single call, return the objects for all IDs given.
To implement the use case, the finder service retrieves all items for the cart from the CartItemHome component. Instead of retrieving each
article when the item is read, the articleIds for all items are collected. Then in a single call, the article data for this set of ID values is read from the ArticleHome component. This also gives the additional opportunity to remove duplicate ID values. Also the article descriptions can be
retrieved the same way from the ArticeDescriptionHome component. Figure 3 shows this scenario.
Figure 3: Optimized approach using lists of IDs as parameters

The figure shows that the same number of remote calls, namely four, is used to retrieve the entire shopping cart as in the joined-tables implementation. But this latter approach obeys the separation of concerns guideline in that it has the advantage of keeping the ArticleHome classes separate from the shopping cart classes. The components do not depend on each other, and so you can more easily replace the article implementations with a new implementation, for example.
Both approaches to reduce the number of remote calls (the joined-tables and the ID-lists implementation) can improve performance considerably by avoiding the set-up times as shown in the theory section. However, as ID-lists implementation obeys the separation of concerns, it is the preferred solution.
This section investigates the application of the facade pattern [DP] to the interface design and how you can use it to save even more remote calls.
Web services involve a specific kind of remote call. Designed to be interoperable, they use XML as the transfer language, and (generally, but not always) HTTP as the transport protocol. Web services are often used to enable remote access to existing service components. Compared to other techniques, XML serialization and deserialization (marshalling and unmarshalling) is rather slow [WSA]. This means a web services call is slower than a local call to the existing component.
The following section investigates the difference between fast and slow calls with the shopping cart example. You will see that a wrapper service that works as facade to the shopping cart, taking remote requests and submitting requests to other services, can improve performance when it reduces the number of slow calls.
Assume the above use case of reading a shopping cart is implemented as a web service (the home components are wrapped in web services, while the user is using a web services client). Figure 4 shows this scenario.
Figure 4: Reading the shopping cart with two web service calls using the web service wrappers (
CartService, ArticleService)
For simplicity, also assume that you have already combined the calls to retrieve Cart and CartItem into a single call to CartHome and that Article and ArticleDescriptions are retrieved through a single call to ArticleHome. The interfaces are designed similar to the ID-lists implementation above.
To implement the use case, the client first performs a remote call to CartService and retrieves the Cart and CartItem objects. It collects the article IDs from the CartItems and then performs a second remote call to ArticleService to retrieve the Article and ArticleDescription objects. As the second call requires
information gathered in the first call, the two calls must be serialized (they can not be run in parallel).
You can optimize this even more by introducing a wrapper service. In the use case example, you implement a CartWrapperService that functions as a wrapper and converts the service call coming from the client to calls to the original Home components. This compound service combines the
CartService and ArticleService calls presented in Figure 4 into a single remote call. Figure 5 presents this setup.
Figure 5: Reading the shopping cart with a
CartWrapperService that calls the original implementation
The figure shows that now there is only one remote call to the CartWrapperService while all other calls are local calls from the facade to the service implementation.
Important is the fact that although the number of local calls (from the CartWrapperService to CartHome and ArticleHome) is not reduced, this interface design saves network latency and transport times for the response of the first remote call and the request of the second remote call.
Although it seems not to address separation of concerns when combining CartService and ArticleService, in fact it does. The original services can still be used independently and have separate concerns. The CartWrapperService component has its own concern -- combining all services to a complete shopping cart (for example, it could also include calls to a pricing service).
In this section, you have seen that moving complexity (combining the calls) from the client to the server helps reduce the number of remote calls. Using this qualitative argumentation, you can see that this coarse-grained communication approach can improve performance compared to a more fine-grained approach.
Number of calls versus caching
When you need to reduce the number of calls further, you can employ caching techniques where applicable in the calling application. Without going into the details of caching service call results, I will give you a negative example from project experience, so you can avoid the described pitfall. In this example, caching was used to overly reduce the number of remote calls, but this approach also resulted in a performance degradation, not improvement. This shows that you must employ caching with care.
In the sample use case, this article has so far only vaguely described the finder for the article descriptions (getArticleDescriptions()) at the ArticleDescriptionHome component. Assume that the article descriptions are language dependent, so they have to be kept in a table separate from the Article table. In other words, for each article there may be more than one description in the ArticleDescription table, one for each language supported (see Figure 1 in Part 1).
The client that calls the service for the use case is now assumed to be a Web-based store application that displays the cart to a user. On the user interface the user can select which language to use. To reduce the number of calls to the ArticleHome component, when a page is read, all descriptions in all languages for the article are read and cached in the calling application. So when the user changes the language, it can automatically be retrieved from the cache, without doing a remote call to the back end.
This approach works for a while. However, when the store goes from three to more than ten supported languages, it becomes unbearable. The time for the initial remote call (retrieving all languages) grows, as does the memory requirements for the cache. Finally, the page is not displayed due to time-out problems in the remote call caused by the excessive amount of data to be transferred.
What happens? The design trades the number of calls for the amount of data transferred. The number of calls is reduced, but each call in itself retrieves a lot of data that is never used. Who will read a catalog in all those languages? So the overall performance is reduced by the additional computation, which could have been avoided by simply retrieving only the descriptions in the language that the user had currently selected.
This section showed that in relation to caching, you should not extend the interfaces to retrieve data that is not needed just to fill the cache. Instead, you should keep the interface small and lean to keep the amount of data to transfer as small as possible, thus reducing the transfer time [JDBCPerf].
This article investigated aspects of interface design for remote services in theory and analyzed some common examples. From this, you can derive generic guidelines for designing distributed components and service interfaces in an SOA context:
- Keep in mind that almost any interface may at some time become distributed. You should especially design interfaces between layers or domains with this in mind, while layer- or domain-internal interfaces might not require this level of attention. Interfaces should, however, be marked as to whether they are designed for remote access or not.
- Keep separation of concerns by separating the interfaces from each other. For example, an interface to retrieve article data does not need to know about shopping carts. This allows for easier reuse of the service in new contexts.
- For retrieving item information, always think about lists of IDs or key values as input, not single IDs or key values. Once the interface goes remote, this allows for improved performance by letting you group requests together (as in the ID-lists implementation example).
- Keep the communication coarse-grained. Any communication path saved is normally an improvement (but keep the data size in mind). In some cases, a wrapper on the remote side might improve performance by encapsulating multiple calls on the remote end into a single remote call.
- Think about the data size transferred, and do not retrieve data that is not needed at a specific time, such as when:
- the chance that you'll need it is small and you cannot foresee needing it in the future (for example, change of user language for language-specific data), and
- it can easily be retrieved by another remote call in case it is then needed.
- Keep your eyes open. Your mileage may vary, so always verify that following the guidelines leads to acceptable results in your specific situation.
Developers often seek performance improvements in the "infrastructure" of an application, like keeping the set-up time low through database connection caching, parallelizing calls, preparing XML parsers or serializers before the actual call, and much more. This article, however, shows that these might not be enough to ensure a well-performing application.
This article investigated interface designs for remote components and services. The theory section in Part 1 showed the different steps of a remote call with the conclusion that reducing the number of remote calls is a main way to improve performance. Part 2 introduced the ID-lists pattern to reduce the number of remote calls and still obey the separation of concerns pattern. Wrapper services on the remote side of the call are another way to reduce the number of remote calls. The caching example showed that reducing the number of calls must be carefully employed. Finally, a set of guidelines for the design of remote interfaces was given.
The discussion illustrated that the interface design has a large influence on the performance. A well-designed interface can greatly reduce the number of remote calls and thus execution time. The remote interfaces should be carefully designed according to the guidelines presented. Nevertheless, the design changes to improve performance have to be carefully monitored to ensure that the right design decisions have been taken.
Of course, good interface design does not guarantee that the application performs well, but it is needed in addition to the usual infrastructural measures.
Thanks to Stefan Peuser, Olaf Zimmermann, Witold Szczeponik and Frank Müller for providing invaluable input and reviewing this article.
- Read Part 1 of this article, "Performance patterns for distributed components and services, Part 1" (developerWorks, August 2004).
- [ SOA] Mark Colan, "Service-Oriented Architecture expands the vision of web services, Part 1 - Characteristics of Service-Oriented Architecture " (developerWorks, April 2004)
- [ SOAD] Olaf Zimmermann, Pal Krogdahl, Clive Gee, " Elements of Service-Oriented Analysis and Design - An interdisciplinary modeling approach for SOA projects" (developerWorks, June 2004)
- [ TopJ2EE] Kyle Brown, Keys Botzum, Ruth Willenborg , "IBM WebSphere Developer Technical Journal: The top 10 (more or less) J2EE best practices" (developerWorks, May 2004)
- [ DP] Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, ISBN 0201633612, Addison-Wesley Pub Co.
- [ SoC1] Edsger W. Dijkstra, "Separation of Concerns"
- [ SoC2] E.W. Dijkstra, A Discipline of Programming, Prentice Hall, Englewood Cliffs, NJ, 1976
- [ AB] WebSphere Studio Information Center
- [ TVO] Deepak Alur, John Crupi and Dan Malks, "Core J2EE Patterns: Transfer Object Pattern"
- [ JDBCPerf] John Goodson, "Performance Tips for the Data Tier (JDBC), Part II: Retrieving Only Required Data"
- [ FLDD] Martin Fowler's First Law of Distributed Object Design: Don't distribute your objects! See "Errant Architectures," by Martin Fowler (Software Development Magazine, April 2003
- [ WSA] Kyle Brown, Rachel Reinitz, " IBM WebSphere Developer Technical Journal: Web Services Architectures and Best Practices" (developerWorks, October 2003)
- Access web services knowledge, tools, and skills with Speed-start web services, which offers the latest Java-based software development tools and middleware from IBM (trial editions), plus online tutorials and articles, and an online technical forum.
- Browse for books on these and other technical topics.
- Want more? The developerWorks SOA and web services zone hosts hundreds of informative articles and introductory, intermediate, and advanced tutorials on how to develop web services applications.
- The IBM developerWorks team hosts hundreds of technical briefings around the world which you can attend at no charge.
André Fachat is an IT Architect in the J2EE community within IBM Global Services AMS in Germany. Although he still knows the machine language of his first computer by heart, his areas of expertise now include Web and J2EE application architectures, distributed computing and modeling. He holds a PhD in theoretical physics from the Technical University of Chemnitz, Germany, where he investigated stochastic optimization algorithms on parallel computers. He joined IBM in 1999 and has since conducted various project engagements, including production references. He worked for IBM in such areas as solution architecture, application development, and consulting.




