Latency is the time that it takes for messages to travel through the network. As most of us know, this value is not zero. For example, in some networking technologies, if a program A sends two messages to program B, it is possible that the messages will arrive out of order. More likely, you may have messages that travel a branched path, where the shorter path actually takes longer. Fallacy 3: Bandwidth is infinite. Despite increasing network speeds, there are still limits to bandwidth; most homes are capped at 1.
These limits can become important in mission-critical applications, since they limit the amount of data you pass around the network over time. The problem with bandwidth is that it is very hard to figure out where it all goes. Plus, there is background noise in an enterprise network caused by email, Web browsing, and file sharing. Fallacy 4: The network is secure.
Security can be a challenge for both administrators and users. These challenges include authentication, authorization, privacy, and data protection. If you are interested in security, you might start your study with the book Secrets and Lies by Bruce Schneier Schneier Then, start working with experienced professionals.
Topology is the physical connectivity of the computers. Failing hardware, handhelds, and laptops can change topology by removing computers, or paths through the network. New wireless technologies allow people to access your network from anywhere in the world. This makes developing applications a complex business, since wireless connections may be slow, they may go away and return, and they may have security issues. You need to think about providing customized interfaces for each type of client.
You also have to think about clients connecting and disconnecting all the time, which can change your data architecture. Fallacy 6: There is one administrator. Large companies have numerous system administrators. The same problem may be solved in different ways, and there are time and version differentials during software updates. Plan for administration and maintenance as much as possible during design time Fallacy 7: Transport cost is zero. In a world were it costs money to move data, developers must be aware of the issues such as quality of service and speed versus price.
Buying bigger hardware and backup networks is expensive. If a solution can be designed in a way that provides the same functionality at a reduced total cost, do it. The business-savvy folks know this, but it is easy to get into a situation where you solve problems with money, not design. On the other hand, if you have the money and it will solve the problem quickly, go for it. Fallacy 8: The network is homogeneous. Networks are a collection of technologies.
As you can imagine, these eight simple misunderstandings can generate a lot of problems and lead to many of the AntiPatterns in this chapter. Hopefully, by identifying and avoiding the AntiPatterns in this book, you will be able to overcome many of these problems and create truly distributed and scalable solutions. The following AntiPatterns focus on the problems and mistakes that many developers make when building distributed, scalable solutions with J2EE. These AntiPatterns often grow out of the fallacies of distributed computing, but can also come from other sources such as time-to-market constraints.
These AntiPatterns discuss some common misconceptions, often presented by nontechnical sources such as managers and analysts, which can mislead developers into choosing a suboptimal basic architecture for their solution. All of these AntiPatterns represent architectural problems that may affect small elements of a solution or an entire network of J2EE applications. These are not problems with code that a single developer needs to look out for, rather they are architecture and 3 4 Chapter 1 design problems that the entire team needs to watch for and keep in mind throughout development.
Localizing Data. Localizing data is the process of putting data in a location that can only be accessed by a single element of your larger solution. For example, putting data in the static variables for a particular servlet results in the inability of servlets on other machines to access it. This AntiPattern often occurs when solutions are grown from small deployments to larger ones. Misunderstanding Data Requirements. Poor planning and feature creep can lead to a situation where you are passing too much or too little data around the network.
Miscalculating Bandwidth Requirements. When bandwidth requirements are not realistically calculated, the final solution can end up having some major problems, mainly related to terrible performance. Sometimes bandwidth is miscalculated within a single solution. Sometimes it happens when multiple J2EE solutions are installed on one network.
Overworked Hubs. Hubs are rendezvous points in your J2EE application. These hubs may be database servers, JMS servers, EJB servers, and other applications that host or implement specific features you need. When a hub is overworked, it will begin to slow down and possibly fail. The Man with the Axe. Failure is a part of life. Planning for failure is a key part of building a robust J2EE application. Once data is localized, it may be hard to delocalize it. This makes your enterprise solution inherently limited in scale.
General Form Localized data is found whenever a single node in an enterprise solution is storing its own data. The problems arise when data that is stored locally needs to be used somewhere else. For example, imagine that you are building a Web site for taking customer orders. Moreover, your initial customer base is small, so you write the solution with a set of servlets that stores customer data in files.
This design might look like the one pictured in Figure 1. Now, imagine that your customer base grows, or perhaps you open a previously internal Web application to your external customers. In either case, you have a lot more traffic than now than you did before, more than your Web server can handle. So you do the obvious thing; you buy more Web servers.
Now you have a problem, pictured in Figure 1. All your customer data is in files located on the first Web server. Files Web Server Figure 1. Symptoms and Consequences Local data is pretty easy to spot. You will be putting data in memory or files on a specific machine. The consequences are equally identifiable. You will not be able to get to the data you need. While EJBs are not supposed to use the file libraries, servlets certainly do, as can other custom applications. Perhaps it is in an Entity Bean that is only available locally within the server.
This is similar to using a static variable, except that you have essentially hidden the data behind the singleton. However, the singleton itself is probably stored in a static variable, which should also be an indicator that something is wrong. A server might even prevent you from doing so, which could break your solution when you upgrade your server. Typical Causes Generally a solution that grows over time, rather than one that was built from scratch, causes this AntiPattern. Often, when the first version of an enterprise solution is built, it is prototyped or implemented in a smaller environment than the one it will ultimately be deployed in.
As a result, local data may not be a problem in the development environment. Using localized data is often the easiest solution. Without the oversight of an enterprise-level architect, individual developers may take the easiest route to a solution despite its long-term negative effects. Developers new to enterprise solutions, and large-scale deployments, may not have encountered problems with localized data before.
They might think that they are applying good object-oriented techniques by encapsulating data, while not realizing that there are larger-scale concepts at work. Known Exceptions Localized data is not always wrong. Sometimes the solution you are building will work fine on top of the local file system, as in the example of a single-server Web site, or when using an in-memory static variable cache. The real issue is whether or not you have to scale access to the data. If the data is used for a local cache, possibly even a local cache of data from a shared location, then having the local data is the point, and not a problem.
If the data is going to be part of an application that might grow, you need to rethink your solution. Refactorings Once you have localized data, the only thing to do is to get rid of it. This means picking a new data architecture and implementing it.
To avoid the problem from the beginning, you should think about how your solution will scale to the enterprise from the very beginning. Distribution and Scaling Variations You might decide to add tiers to your application as a way of changing the data architecture. Adding tiers, or new levels to the solution, can take the form of databases, servlets, JMS servers, and other middleware that separates functionality into multiple components. Adding tiers is basically changing the data architecture from a functional perspective. By changing the data architecture, you may be improving your solutions scalability.
Originally, all of the customer data was on one Web server, stored in files. While you can copy it, as pictured in Figure 1. You might even corrupt your data if two servlets from different servers perform mutually exclusive operations on the customer data before their files are synchronized. In this design, pictured in Figure 1. To prevent corruption, you can lock the files to protect them from simultaneous access. This last step demonstrates the basic idea of changing the data architecture; you have to move the data away from a single server to a shared location. If you are familiar with relational databases, you might notice that this idea of file sharing and locking gets pretty close to what you use a relational database for.
So, the next step in the solution is to move up to a formal database, pictured in Figure 1. This will include the locking, but also add transactional support to your solution. As you can see, changing the data architecture can be an iterative process where you start with one solution and move towards a heavier, yet more scalable solution. Any solution may have localized data that has to be refactored.
Misunderstood requirements can lead to localized data and other problems such as underestimating bandwidth requirements, and the approaches to resolving it are similar. During design, it is easier to get a handle on simple records than complex, multivalued records, so designers will often think in terms of the simplified records. This AntiPattern, Misunderstanding Data Requirements, can affect the final solution in terms of both the size of data and the performance requirements for parsing and operating on it.
These incorrect assumptions can then affect your entire distributed solution by changing the amount of network bandwidth used, causing you to underestimate the time it takes for each endpoint to do its work, or causing some other effect. General Form Misunderstood data requirements can take many forms, but there are two main ones.
First, developers might use small data sets for development because they are easy to manage, but upon deployment, the real data sets may be much larger. This can result in bad performance on the network and in specific applications. The second situation manifests when more data is passed around the network than is actually needed. For example, suppose that a customer service application represents customer data in an XML file. Moreover, that XML file contains all of the information about a customer.
Now, suppose that one step in your J2EE application needs to confirm that a customer number is valid, and that you implement this check in an EJB. Do you need to pass the entire customer record, possibly over a megabyte of data to the EJB? The answer is no; you can just pass the customer number. But if you choose the wrong application design, you may have to pass the entire record to every node in the solution. They come into play when you really deploy your solution. The following issues are important to look for. This can indicate that you misunderstood the data, most likely indicating that the size changed noticeably.
You may have way more available bandwidth or processing power than you need. This can lead to a higher project cost than was actually necessary. Distribution and Scaling Typical Causes Generally, misunderstood requirements are caused by bad design, like the following issues. When you create a data architecture that relies on messages carrying all of the data, you will often send more data than each node in the architecture needs. Known Exceptions Misunderstood data requirements are not acceptable, but the symptoms of misunderstood data requirements can arise when you create the same situations on purpose.
Or you might know that the data sizes will change at deployment and have planned for it, even though the data sizes you use during testing are much smaller. Fundamentally, the solution is to understand the data requirements. To do this, you have to make realistic bandwidth calculations, which requires realistic data calculations.
Second, you have to look at what information each part of a distributed application really needs. Then you have to pick the right data architecture from these requirements. Variations The main variation of the Misunderstanding Data Requirements AntiPattern is overestimating data rather than underestimating it. Or in the case of passing too much data, passing too little data. This can occur when you try to optimize too early, and end up in the situation where an endpoint in the solution needs more data than you initially thought.
Example If we take the example in the general form, in which customer information is stored in XML files and we need to validate the customer number using an EJB to encapsulate 15 16 Chapter 1 the validation process, then the obvious solution is to not require that the EJB get the entire customer record. This means that any part of the application that needs to validate the customer number must get the number, call the EJB, receive the results, and use those results appropriately.
If you want to have a piece of the application that makes a decision on how to proceed based on the validation, that piece can take the full XML, extract the number, validate it, and proceed accordingly. If you need the validation EJB to pass more data to one of its service providers, you must design the EJB interface to take the additional data with a request so that it can include that data in the messages it sends.
Related Solutions Misunderstood data requirements can easily grow out of the Localizing Data AntiPattern and can easily lead to the Miscalculating Bandwidth Requirements AntiPattern, which are also found in this chapter. These three AntiPatterns are tightly linked and have similar solutions. When misunderstood data leads to miscalculated bandwidth requirements, it can become the underlying cause of a performance failure.
Similarly, data rates are often mimicked for the initial testing at much lower requirement levels. Unbalanced Forces: Creating a solution in a development environment that is truly representative. Taking the time to evaluate the actual requirements, not just sketch out a simple solution. For example, if you think that all of your JMS messages will be 1 KB and they are really KB, you are going to use times the network capacity than you planned. Often a development group might be given a requirement of x number of transactions per second, where x is small, such as 5.
But, the size of the data being passed around may be very large, such as 50 MB. So, the actual bandwidth requirement is Mbps which is beyond the abilities of the basic Ethernet card, and probably beyond the processing power of a small to midsize server. General Form The Miscalculating Bandwidth Requirements AntiPattern generally appears when the size of the data that is sent between nodes or tiers in a solution is large.
For example, an order management solution that passes data about the orders in XML files might look like the solution pictured in Figure 1. If the orders are small, say under KB, then a typical Ethernet system, with 10 Mbps, can certainly handle tens of these orders each second. In this example, there are five nodes that have to process each order. Further, we would expect that each node takes some amount of time to do its work. Now, there might be several orders moving between nodes at one time, as pictured in Figure 1.
We are still meeting our requirement, and assuming that we have small, KB messages, so we are probably still okay. But what if the orders are bigger? Suppose that the orders are really patient records, or include graphics, or historical data, as these enterprise messages often do.
So, now the records are 1 MB or even more. The solution in Figure 1. Our bandwidth is starting to get into trouble. Each message is now a noticeable percentage of a normal Ethernet network. Filling up the network will slow the overall performance of the application and all others distributed on the same network. This can lead to message backup, which may require quiet times to allow the systems to catch up. This can even happen on systems that support persistent store and forward methods.
One example of this, which I have seen in practice, occurs when the persistence mechanism runs out of space. Even a 2-GB file size limit can be a problem if the messaging infrastructure is asked to store more than 2 GB of messages. With 5-MB messages, this is unlikely, but it is possible with larger messages. Lost messages are a terrible symptom of overwhelmed bandwidth and can cause dire consequences with lost data.
This causes a rippling effect, where each application is degraded due to no fault of its own. Typical Causes Generally, bandwidth issues arise from poor planning. During design, if the developers thought that messages would be KB and they turn out to really be 1 MB, there is likely to be a problem. Often designers can make a good guess at average message volume, but forget to think about peak volume.
Systems that use a JMS server, Web server, process automation engine, or some other hub may wind up with more network hops than an initial design might show. For example, in a process automation design, each step in a business process will often result in a network call and response, rather than a single call. When a J2EE solution is installed on an existing network, there is already traffic flying around.
If that traffic is taxing the network, implementing a new solution in combination with all the existing ones may overload the network. Installing numerous J2EE services on one machine means that a single network card may be forced to deal with all of the network bandwidth requirements. This single card becomes a choke point that must be reevaluated. Network messages take time to get from one place to another.
You may encounter situations where machine A sends messages to machines B and C, and machine B sends a message to machine C after it receives the messages from A. But because of the network layout, the message from A to B to C gets there before the message going from A to C directly. Timing can play a key role in your application. Known Exceptions This AntiPattern is never acceptable, but it may not be applicable for simple solutions on fast networks. This is where planning is important. If you are building a small departmental solution with reasonably small messages on a Mbps network, your requirements may be far below the available bandwidth.
Also, developers lucky enough to have gigabit networks for their deployments are more insulated than a developer on a smaller department-sized network. Refactorings The first solution to bandwidth problems is to perform reliable bandwidth analysis at design time. The other solutions to bandwidth problems are split between design time and deploy time. If you think you have a large data requirement, you should think about the data architecture you are using.
Could the customer data go into a database, thus reducing network traffic? Also, could each node only retrieve what it needed, rather than get the entire order document? You can also add hardware to the situation. Build special networks for subsets of the solution or buy a faster network. Both of these are valid solutions and may be required for very large problems that simply push the boundaries of network technology.
But in many cases, you can use architecture to solve the bandwidth problem. Variations The main variation on the single network hog is a group of applications that combine to create a high bandwidth requirement. Suppose that our orders are just too big, and there are too many for the network to support. We might fix this by revamping the system to use a database to store order data. In this case, the first node in the system would store data in the database.
Other nodes, as pictured in Figure 1. Note there are still messages being sent from one node to the next, but these can be very small notification messages instead of larger, bandwidthhogging data messages. Keep in mind that when we redesign the data architecture, we might be adding network hops. In this case, we are replacing one network hop from one node to the next with three network hops, one from the node to the next to say go, and two to get the data from the database.
If the total requirements of these three hops exceed the original one, we have made the problem worse, not better. For example, if the original hop would have been 1 MB of bandwidth, but the new set of hops is 1. Related Solutions Another possible solution is to look at store and forward methodology as a way to control the times that messages flow through the system. For example, you might optimize for order entry during the day, while optimizing for order processing at night.
This means that one piece of software is acting as a hub for data or processing destined for other software in the solution. These hubs are often hidden or overused. Overuse can lead to overall system performance problems. For example, a database or rules engine will often be a hub. As a result, it must be taken into account for all performance and scalability requirements. Often hubs represent bandwidth hogs, so the architecting you do to identify your bandwidth requirements will also help you identify the hubs.
In order to create scalable hubs, you may need to architect your solutions from the beginning with that scaling in mind. This means that the biggest task, from the start, is finding your hubs. Once you know them, you can plan for how they can grow. Drawing a picture that shows the various connections in your J2EE solution can identify the first form, where a single component or machine has a very large number of connections to it. In this case, connections mean two machines or applications talking to each other.
An experimental classification web service
You must capture the exact path of data across the network. For example, in Figure 1. We have even included the router in the picture to show that it, too, can be thought of as a hub. Seeing a picture like this should draw your attention to the database and the possibility that it is going to be or has become overworked. Custom Application Distribution and Scaling The second form of an Overworked Hubs AntiPattern can be found when you add bandwidth information to the network picture.
If we updated Figure 1. In the first version, the database is still a suspect since it is requiring a lot more bandwidth than the other nodes. In the second figure, the bandwidth requirements have changed, and now the JMS server looks like it is more at risk of overwork than the database server. As you may have surmised from the examples, this second form of an overworked hub occurs when a single node has to process a lot of data. The node may not have a lot of connections, but its data requirements may be so high that even a few connections can overload it.
Symptoms and Consequences Hubs are too important to mess up. This can put a strain on the endpoint, which could cause it to lose data. This could result in lost events or late events coming from the external system. Typical Causes Generally, problems at hubs can appear at any time in the process. The following causes are examples of these different time frames. For example, budget constraints can happen early in a project, while changing requirements happen later. When a system is put into operation the hardware and architecture may be more than able to support the daily requirements.
However, if more customers, transactions, or requests are added to the system, some machines that have been acting as hubs may be unable to keep up with the new requirements. Sometimes the scalable solution may involve hardware or software that is more expensive.
A poorly planned or highly constrained budget could result in the purchase of less than ideal technology support, which could lead to overworked hubs. Companies that buy software that inherently creates hubs—such as messaging, database, application, and Web servers—are likely to reuse them for multiple applications. This is a perfectly reasonable plan under the right circumstances. However, as new uses are found for existing software and hardware, and new clients are added to the hub, there may come a point where the current implementation of the hub is not able to handle the new clients.
For example, a router that will come into play during the final deployment may be missing from the development or test environment. If the router is there, it may not be under the same load in the test case as it will be in the deployment case. This can lead to hubs that are actually going to be overworked in the actual solution that are not identified until too late.
Known Exceptions It is never okay to have an overworked hub. It just means that you have to plan for them. Some architects try to create completely distributed solutions with few or no hubs. This approach may avoid overworking a single machine, but it creates two undesirable side effects. First, total distribution is likely to create much higher and possibly less-controlled bandwidth requirements. Second, systems that are highly distributed are harder to maintain and back up. So, while you can have a system with three endpoints all talking directly to one another, you may not want endpoints doing the same thing.
With many endpoints, it is easier to design, manage, and administer the system if you add hubs to focus the connections. The first thing you can do is to avoid the problem during design time. This requires really planning for scale and looking at realistic bandwidth requirements. You should also draw network diagrams to look for hidden hubs. The second thing you can do is to fix the problem when it arises.
There are a couple ways to do this. First, you can add hubs. Adding hubs may require some architectural changes. Generally, hubs are added transparently or nontransparently. In either case, you can think of adding hubs as load balancing since you are distributing the work or data load across several machines.
Nontransparently adding hubs will often involve partitioning data. This means that the work and data from one subset all go to one hub, and the work and data for another subset all go to another hub. An example might be storing North American data in one database and European data in another. Partitioning data is perhaps 27 28 Chapter 1 the most common form of nontransparent load balancing. This means that you are balancing the load between multiple hubs, but there is some knowledge of the data or work going into the balancing decisions.
Detecting Performance Antipatterns in Component Based Enterprise Systems
You can add hubs transparently when you have the special situation where all hubs are equal. This might be the case on a Web server for example, where all the servers might have the same Web site. In this case, you may be able to add hubs and load balance across the set. When hubs are equivalent, you can do transparent load balancing, which means that you can just add hubs all day long to fix the problem. When all of your hubs are equal, you throw hardware at the problem, which is perhaps the easiest solution.
It may or may not cost more to use this technique than the other, but in a lot of situations, it can be done with little rearchitecting. Where redesigning costs time, hardware costs money. If the redesigning is quick, the time is less costly than the hardware.
If the redesign takes a long time, it may exceed the cost of new hardware. Neither throwing hardware at a problem nor adding hubs has to be purely reactionary. These fixes can be part of the original plans. Variations One variation of an Overworked Hubs AntiPattern is a database or external system connection that only allows single-threaded access. This connection might not support sufficient response times and appear to be an overworked hub in your final solution.
Both have potentially overworked hubs. The first has a database with several clients; the second shows a JMS server with a large bandwidth requirement. Taking the database example first, we need to add support for more clients to the database node. There are two ways to do this. First, we could just use a bigger machine. That might work and could be easy to implement. The second thing we could do is partition the work.
Depending on our database vendor, we might be able to use some kind of built-in load balancing, as pictured in Figure 1. This could require rearchitecting the solution and possibly adding a hub that does less work, making it able to handle the same number of connections as the original database.
This solution is pictured in Figure 1. We can attack this server with the same techniques: first, a bigger machine, then, load balancing if the server supports it. If neither of those solutions works, we can then try to partition the messages. Repartitioning a JMS server with preexisting data and connections could cause a few main problems. First, you may have to change the connection information for existing clients. Second, you may have messages in the JMS queues that are stored in the wrong server.
Some specific tight links exist between hubs and bandwidth requirements and hubs and dislocated data. In both of those situations, the data or the bandwidth is often related to a hub. Similarly, overworking a hub often involves placing high bandwidth or data requirements on it. Two horror stories pop into my head. The first happened in Grand Rapids, Michigan, while I was teaching a class. Things were going well when suddenly all of the power went out. We looked out the window, and there was a man with a backhoe cutting the main power line to the building, which housed the data center.
The second story happened in San Jose, California. Someone took a backhoe to a main fiber-optic line that belonged to the phone company. It took days to hand-splice the thousands of fibers back together.
Both stories are testament to the fact that network programming has risks that go beyond those of single computer programming, and you have to plan for them. I like to motivate myself by thinking of a person with an axe who likes to cut network, and other, cables. He may do it a number of ways: He might kill the power supply; he might break a network connection; he might decide to crush a router or install a bad software patch on it. Symptoms and Consequences Network failure is pretty devastating when it happens, so the symptoms, such as the following two, should be easy to spot.
Network failures can stop the application from working. If a key database is down or offline, then the entire application may be unavailable. This can indicate a problem in the network or at that machine. Either way, you may have serious repercussions at the application level. There are a couple of things you can do to avoid this, such as creating redundant lines, but even these extreme preparations may not succeed. Hardware can fail. Perhaps a hard drive crashes, or a power supply dies, or some memory goes bad.
In any case, hardware is reliable but not infallible. Often new software, or even software currently running, may have problems. When this happens it can take one piece of your J2EE solution out of commission. Sometimes everything is fine, except the amount of available resources. For example, a disk may become full on your database server. This can lead to massive problems, even though no one component can really be blamed. They can have a computer or a network of computers. These people can break single machines, break groups of machines, or just bog down your network.
Known Exceptions While there is no perfect solution, you can certainly restrict your planning to the level of consequences that a failure would bring. For example, if you are an Internet service provider that charges people for making their Web site available, then you need to have very high reliability. Refactorings The two applicable refactorings — both found in this chapter — are Be Paranoid and Plan Ahead.
You have to plan for and assume failure. You have to be paranoid. You have to look at your system and analyze it for failure points. You also have to think about how important a failure is to determine the amount of work it is worth to try and avoid failures. On the other hand, application servers became modular.
Glassfish v3, with its hk2 and OSGI architecture, is able to load and unload its modules on the fly.
Even the admin console is loaded on demand. So in Java EE 6 the web and business logic could fusion together and become "monolithic" - the application servers become modular. Something like "Inversion Of Best Practices" Nonetheless - it's good stuff in both cases. Instead of layers, more and more architects, fall in love with modules and plugins right now. Let's wait for the next "Inversion Of Best Practice" As simple as possible, but not simpler. Not at all. I still use an abstract class with class type and ID for generic methods such findById, findAll, merge, etc, in this way I don't [ AntiPattern: Ignoring Reality.
AntiPattern: Too Much Code. AntiPattern: Embedded Navigational Information. Introduce Traffic Cop. Introduce Delegate Controller. Introduce Template. Remove Session Access. Remove Template Text. Introduce Error Page. Chapter 5: Servlets.
AntiPattern: Template Text in Servlet. AntiPattern: Not Pooling Connections. AntiPattern: Accessing Entities Directly. Introduce Filters. Use JDom. Use JSPs. Chapter 6: Entity Beans. AntiPattern: Fragile Links. AntiPattern: Surface Tension. AntiPattern: Coarse Behavior.
- The Comparative Physiology of Regulatory Peptides.
- Account Options;
- J2ee Antipatterns.
- Computer Modelling of Fluids Polymers and Solids!
- Joels Use of Scripture and the Scriptures Use of Joel (Biblical Interpretation Series)!
- 2 editions of this work.
AntiPattern: Liability. AntiPattern: Mirage. Local Motion. Flat View. Strong Bond. Best of Both Worlds. Chapter 7: Session EJBs. AntiPattern: Sessions A-Plenty. AntiPattern: Bloated Session. AntiPattern: Thin Session. AntiPattern: Large Transaction. AntiPattern: Data Cache. Split Large Transaction. Chapter 8: Message-Driven Beans. AntiPattern: Overloading Destinations. AntiPattern: Overimplementing Reliability. Architect the Solution.