J2EE design strategies recommend that all business logic exposed to EJB clients should be placed in Stateless Session Beans. See Wrap Entity Beans with Session Beans (http://www.theserverside.com/patterns/thread.jsp?thread_id=625
) pattern for more information.
Message Driven Beans can be considered a variety of Stateless Session Beans and thus perform similar tasks. The difference can be argued lies in the Bean invocation. However, Object Oriented and J2EE design principles call for a clear separation of responsibilities between Message Driven and Stateless Session Beans. MDBs should serve as asynchronous message consumers. They should receive, process and parse the messages but should not perform any actual work. As discussed above, all the business logic should be located in Stateless Session Beans. MDBs should interact with specific methods from these Beans instead of implementing the functionality themselves. Note that MDBs should not interact directly with Entity Beans but rather allow Stateless Session Beans to do this as a part of a business logic method.
This technique strengthens and supports previously defined J2EE design patterns by containing business logic in one logical location. It also allows MDBs to become easier to implement and more lightweight and efficient by separating message parsing from business logic implementation.
I think this pattern is very useful in certain situations, which are fairly easy to spot. However, I disagree with Leo about the scope of this pattern. Here is why:
"J2EE design strategies recommend that all business logic exposed to EJB clients should be placed in Stateless Session Beans. See Wrap Entity Beans with Session Beans".
First of all, the meaning of the term "business logic" depends on the context in which it is used. In the context of the pattern mentioned, it means logic that is called directly as a result of a client request (this is the only model in EJB1.1, for which the pattern was designed). This kind of business logic may be different from the "backend service logic" you might put in MDBs.
Second, this pattern was designed for EJB1.1, and MDBs were not an option. I don't think the pattern completely covers the new possibilities in EJB2.0 (e.g, MDBs).
I do not think MDBs and SLSBs perform logically equivalent tasks. IMO, The a fundanmental difference is in the context of the executed task. MDBs perform tasks in an independent context, while SLSBs perform the task in the same context (context meaning TX, security, etc).
The idea of seperating message consumption from logic is good OO thinking. However, I can't see any reason why this seperation should divide the two tasks into different phisycal layers (i.e, different containers). There are many design pattern that facilitate this seperation without this division (i.e, event-dispatch, command).
"This technique strengthens and supports previously defined J2EE design patterns by containing business logic in one logical location."
Can be done with regulaer patterns without adding this layer of physical indirection. Old patterns were not designed with MDBs in mind (EJB1.1), so I don't think breaking them is a problem when dealing with MDBs.
"... It also allows MDBs to become easier to implement and more lightweight and efficient by separating message parsing from business logic implementation."
How does the implementation become lightweight? Instead of one MDB you have an MDB and a SLSB, which is probably heavier as far as the container is concerned. As for efficiency, the only thing you do is add the extra SLSB call, so the new implementation would probably be less efficient.
The scope where I think you should use this pattern is where you want to encapsulate "system functions" which are generally useful for both backend and frontend components. In other cases, I think keeping the code in the MDB will make the execution context of the code clearer, and produce more efficient implementations.
The response made a number of assertions that I will try to dispel.
1. "First of all, the meaning of the term "business logic" depends on the context in which it is used."
This is not necessarily true. Business logic, for most part, should be independent of its context. Whether a business logic method is invoked from the client directly or from a client-independent entity like an MDB, the result should remain the same. For example, if you are writing an e-commerce software and need to allow the clients to store order information either via a direct call or in response to an asynchronous message, business logic encapsulated in SLSB(s) should perform the same operations no matter how it is accessed.
2. "Second, this pattern was designed for EJB1.1, and MDBs were not an option."
The second part of this statement is true – MDBs were not available in EJB 1.1. Thus, this pattern is only applicable to EJB 2.0 exactly for that same reason. Also, as explained above, business logic in majority of situations should not depend on the calling context. Therefore, this pattern applies well to EJB 2.0.
3. "I do not think MDBs and SLSBs perform logically equivalent tasks."
I agree. Read my statement from the original post. It clearly differentiates between logical tasks performed by both types of Beans.
"Message Driven Beans can be considered a variety of Stateless Session Beans and thus perform similar tasks. The difference can be argued lies in the Bean invocation."
4. "I can't see any reason why this separation should divide the two tasks into different physical layers."
If we agree on the fact that business logic does not necessarily depend on its invocation context, as argued in the point 1 above, then the concept of keeping it in one logical layer, namely Stateless Session Beans, stands true. This clearly delineates the roles that MDBs and SLSBs play in the whole EJB hierarchy. MDBs are to be used to perform message handling, while SLSBs are to provide a repository for business logic methods. If we use the example given above, your suggestion would be to place different order handling logic into MDBs and SLSBs. This approach does not promote reuse, as we would end up with slightly different implementations of the same code. The whole idea behind the concept of shareable business logic is to establish a logic layer encapsulating functionality that can be reused by a variety of clients -- whether they have execution context or not. If business logic is encapsulated inside the MDBs, it becomes inaccessible to other clients and thus loses all its reuse potential.
5. "How does the implementation become lightweight? Instead of one MDB you have an MDB and a SLSB, which is probably heavier as far as the container is concerned."
First and foremost, this is a design pattern that encourages code reuse and minimizes maintenance. I agree that this pattern does not increase performance, but it does not decrease it either. If we are to implement business logic inside the MDB instead of SLSB(s), the difference lies only in the instantiation of the local classes rather than the Session Beans. If these Beans are located in a remote container, the performance is decreased by the amount of RMI calls that need to be made. Efficiency, however, can be improved if SLSBs are located in the same container as the MDB. Regardless of the performance, implementing the MDB becomes a more efficient process since the only things developers need to do are parse the message and invoke the correct business logic methods from SLSB(s). This simplifies the developer’s job and decreases the amount of future maintenance necessary.
6. "The scope where I think you should use this pattern is where you want to encapsulate "system functions" which are generally useful for both backend and frontend components. In other cases, I think keeping the code in the MDB will make the execution context of the code clearer, and produce more efficient implementations."
As argued in point 1 above, no difference in business logic implementation should be made whether it is invoked from a front-end or back-end component. Thus, the scope for this pattern has been correctly defined. Furthermore, the execution context can and should be taken into account before a business logic method is invoked. As mentioned above, shareable business logic should produce the same results regardless of the context. Context may be a part of its arguments but by no means should dictate its functionality. In fact, separating execution context from business logic is a good OO approach. I agree that keeping the code in the MDB will make it more efficient but it will produce a more tightly coupled, less reusable and harder to maintain code.
I think you misinterpreted my argument in (1). I didn't mean that specific business logic, e.g "place order" depends on the context. What I ment was, the *term* "business logic" depends on the context. When the pattern you mentioned sais "business logic" it originally ment a certain type of logic (something that would fit in the controller of MVC). I don't think the pattern is valid for all types of logic (anyway, you can't rely on the pattern to say that. If you think it is still valid you should explain why yourself).
As for the part about logic not being influenced by sync/async, I don't think it is as clear cut as you put it. In fact, it is quite similar to the "remoteness is an interface" debate. While it seems on a first look that remoteness does not affect the interface (as reflected in many early RPC systems), reality is a bit more complicated. Whether or not you can write code to operately correctly regardless of remoteness (or transaction context for that matter) is a philosophical question, and I think you can't. In fact, some people would argue that the lack of distributed transaction context sharing is the most fundenmental difference between messaging and synchronous processing (see JMS spec, page 89).
As for what you said about seperating logic from the invokation mechanism, I completely agree with you that it is good OO. However, the seperation you suggest is an architectural measure. As I said in my post, I see no reason to use this kind of measure. SLSBs and MDBs live in logically seperate containers. Even if they actually execute in the same container, they still shouldn't share memory or any other resources. All interactions between them (even in the form of side-effects) should go through the container. This, IMHO, is not the appropriate measure to use to get code reuse and modularity. Normal OO patterns (as I mentioned: command, event-dispatch, etc) can achieve the same goal without seperating the environments. They achieve everything you mentioned in your post without adding another division.
In fact, I think the most highly normalized and OO correct approach would be to isolate all the logic into an object, and invoke that object from your SLSBs and MDBs that need it. If these two types of EBs represent alternative invocation approaches, why give SLSBs a priority putting the code directly in them, and make MDBs call them? IMHO it would be much better OO design to indirect the functionality out of them both, creating a clearer hierarchy.
That said, I doubt people will use this normalized design for all their code. While it has theoretical advantages, it is quite cumbersome. I think for 80% of the logic people write, they will have a very clear idea about where it is going to execute (MDB of SLSB), and they won't go to all that trouble. One of my favorite Eckel quites seems to capture this, "50% of the art of OO design is in understanding that every design problem can be solved by adding a new level of indirection. The other half is in knowing when to stop indirecting".
This is a response to Gal’s second message.
Let’s review each argument separately. You make some good points but other statements need to be clarified.
1. "What I ment was, the *term* "business logic" depends on the context. When the pattern you mentioned sais "business logic" it originally ment a certain type of logic (something that would fit in the controller of MVC). I don't think the pattern is valid for all types of logic."
I think the meaning of the term “business logic” should be better defined. The pattern and all of my statements are based on the concept of shareable business logic. From the design perspective, this is a piece of code encapsulating a set of business rules that need to be applied to data in order to obtain necessary results. This code should be immutable across applications and implemented in the way to support this concept. Business logic should be shareable amongst multiple systems and produce the same results regardless of the data or context. For example, returning to the place order example used earlier, basic operation of saving an order (store it in a database and return an order number, for instance) should be performed the same way whether it is called from a JSP page, MDB, Web Service, or any other client. Execution context plays an important role in the call flow but it should be handled one layer of abstraction above the business logic tier. In this particular pattern, MDB handles all of the execution context details and only calls business logic components in the appropriate spots of the flow. If we take a direct call example, execution context should be handled by JSPs, servlets, Java Beans, or possibly Stateful/Stateless Session Beans that invoke business logic methods to complete the operation.
The concept that I keep trying to explain is very simple. Every organization has a set of business processes and rules that it follows to perform its daily tasks. These represent business logic and should be shareable across applications since their scopes and needs tend to overlap. Business logic should be independent from the execution context because multiple applications need to access it and attain the same results. If business logic is, in fact, tied to the execution context, it ceases to be shareable and can only be used in that particular context. Therefore, you should handle particular context details one layer of abstraction higher. The concept that I described above can be further generalized to any software. The business logic layer becomes a set of components whose functionality is shared across application(s) or other components.
2. "As for the part about logic not being influenced by sync/async, I don't think it is as clear cut as you put it... Whether or not you can write code to operately correctly regardless of remoteness (or transaction context for that matter) is a philosophical question, and I think you can't."
Coding logic is always influenced by the execution context. However, business logic, as explained above, should not be. There are instances where your statement is true. I argue, however, that if you adhere to the concepts of business logic and layer separation, you will find yourself writing less proprietary code and reusing the existing one to a larger degree.
3. "SLSBs and MDBs live in logically seperate containers. Even if they actually execute in the same container, they still shouldn't share memory or any other resources. All interactions between them (even in the form of side-effects) should go through the container. This, IMHO, is not the appropriate measure to use to get code reuse and modularity."
Whether SLSBs live in the same container or even on the same server as the MDBs is more or less irrelevant. The invocation of a SLSB from an MDB remains the same – you find the Bean in the container, get its home interface, and instantiate the remote interface. The only things that are shared between the two Beans are arguments passed from one to another.
4. "In fact, I think the most highly normalized and OO correct approach would be to isolate all the logic into an object, and invoke that object from your SLSBs and MDBs that need it. If these two types of EBs represent alternative invocation approaches, why give SLSBs a priority putting the code directly in them, and make MDBs call them?"
This is an interesting approach but it does not promote sharing of business logic. It does provide a base for code reuse but not the functionality reuse. Note a significant difference here. Code reuse concept relies on the fact that the code is developed and tested once and then can be incorporated into other modules or applications. If the shared code changes, all of the other code relying on it needs to be changed and/or recompiled. Functionality reuse is defined by binary component sharing. This means that once a component is developed, tested and deployed, other modules, components or applications can access its functionality. If it is changed, nothing needs to be done in the code relying on it. Additionally, shared components can be placed in a centralized location, which simplifies maintenance and administration. The business logic concept that I keep referring to assumes functionality reuse rather than the code reuse. Thus, SLSBs are utilized as business logic repositories since they provide a convenient and simple functionality sharing mechanism.
This is a response to Leo's post. I won't address each point specifically because this discussion is begining to bloat up, and I think the entire debate revolves around one fundenmental point.
In 4, you make a good point about functional reuse, and I completely agree. To a large degree, functional reuse is the motivation for component based design. So when you want functional reuse, you should put the functionality in a component. Now, as for the type of the component (there are many component systems, and atleast a couple in J2EE), you should decide based on whatever is most appropriate. I wouldn't say that SLSBs are *allways* the best type of components, but I do agree that it is usually a good choice. Some types of functionality are more naturally described as an asynchronous process, and MDBs may be more appropriate for them. So I don't really accept SLSBs as "the way" to get functional reuse. I think this pattern would better advise designers by saying that when you need functional reuse, pick an appropriate component type (and advise SLSBs as a common case).
That said, the specific argument I tried to make in (4) is different. In your original post, you said:
"... by containing business logic in one logical location. It also allows MDBs to become easier to implement and more lightweight and efficient by separating message parsing from business logic implementation."
I agree that the points you outline here are important. However, I think it is misleading to say that, when you want these things, you should use this pattern. Creating a logical seperation of code, seperating different tasks (in your case, parsing from logic), etc are all characteristics of Object-based design, *code reuse* and modularity. Code reuse, not functionality reuse, is the case here. And when you want the things above, you shouldn't break code into components. You should break it into objects.
I think that this pattern is appropriate when what you actually want is *functionality reuse*. You said it in your post:
"Thus, SLSBs are utilized as business logic repositories since they provide a convenient and simple functionality sharing mechanism."
I said the same thing, in different words (trying to get down from the high-level words and give a real description people can use to decide if this pattern is appropriate for them):
"The scope where I think you should use this pattern is where you want to encapsulate "system functions" which are generally useful for both backend and frontend components"
I was relating to specific components (backend, frontend) but the point is, use this pattern where you have functionality (logic) that should be accessible to all parts of the system in a unified way, not specific to one component of your system.
So to conclude, I think the scope where this pattern is useful is for functionality reuse of code that happens to run in MDBs. I don't know if that makes a pattern, because after all component are, by definition, tools to help you achieve functionality reuse. Anyway, the rest of the stuff about modularity, clearer code, logical seperation, efficiency, etc are not in the scope. They can and should be achived by structural OO techniques, not components. That's what I tried to emphasize with the quote in my post above: over componentization is just as bad as no componentization. Only divide code into seperate components where you need them - for functionality reuse. In other cases, you just don't need this pattern.
This is a response to Gal's post above.
We both agree that SLSBs may not always present the best avenue for storing business logic but in majority of circumstances they provide the most suitable location. I do, however, disagree that when implementing MDBs "you shouldn't break code into components". I presented a number of examples in my earlier posts that try to underscore situations, in which various clients and MDBs would need to use the same business logic, thus promoting the need for functionality rather than code reuse.
Reading your messages and my pattern more carefully, however, I think I understand where you see the problem. The issue concerns primarily this statement, which you quote in the above post: "... by containing business logic in one logical location. It also allows MDBs to become easier to implement and more lightweight and efficient by separating message parsing from business logic implementation." I agree with you that in order to make the MDBs "more lightweight and efficient", you would not want to call a SLSB since it would decrease the performance. My original statement should have read as following: "... by containing business logic in one logical location. It also allows MDBs to become easier to implement and development process more efficient by separating message parsing from business logic implementation." This should clear up any confusion.
I think we agree on the rest of the points made throughout the discussion. To make it clearer, this pattern comes down to a few simple directives:
1. When an MDB needs to access shared business logic, it should be done through a layer encapsulating this functionality, which most often is placed into SLSBs.
2. Do not use #1 if specialized logic needs to be implemented in MDBs.
This pattern promotes functionality sharing through the use of components, specifically when applied to MDBs. Functionality reuse is one of the key premises of component architectures but proper design techniques should be used to maximize sharing and reuse.
I completely agree with the succint description you made (with points 1 and 2) of where this pattern should be used. I think, from reviewing the disucssion, that our disagreement was mostly semantic.
Just to be on the safe side, there is one more comment about your last post:
"I do, however, disagree that when implementing MDBs "you shouldn't break code into components" "
I don't think so either. I think that if *all* you need is logical seperation of code, modulary, etc you shouldn't break code into components. You should break it into objects. A component should be used when you need to reuse the functionality, i.e when the functionality is shared by several parts of the system. I was just trying to say that logical seperation and modularity aren't good enogth an excuse for using a seperate component :)
" "... by containing business logic in one logical location. It also allows MDBs to become easier to implement and development process more efficient by separating message parsing from business logic implementation." "
I still think this is flawed. As I mentioned above, the logical seperation is not the reason this pattern should be used. Objects would suffice for that. Same goes for seperation of parsing from logic. I think a more accurate description would be:
"... by encapsulating sharable business logic in a seperate component. It also allows MDBs to share functionality with other components in the system rather than duplicate it, leading to a more manageable system"
I do think that the pattern, the way you put it in your last post (point 1-2), is indeed useful. I have used it myself a couple of times, and it is a "recurring best pratice" which makes a "pattern".
However, it really should be obvious to a designer that in order to reuse functionality he/she should break the functionality down to a seperate component (that's what components are for). So I think the value of the pattern is in advising SLSBs as a good common-case solution.
I should add here that typically I use Java classes (not SLSBs) to encapsulate my MDB business logic.
Typically, my MDBs are called by Java clients initiated by cron - usually to FTP and/or process large files.
By using fairly decoupled Java classes, I can easily write a command line tool to perform the same task, and then re-use the business logic inside the MDB.
This means that if for any reason the MDB failed, I can rerun the process on the command-line if necessary to sort out the problem.
The pattern does not promote logical separation of code but rather business logic sharing. It tries to outline situations, in which an MDB needs to access already implemented business logic functionality. Rather than instantiating classes containing the code, an MDB should access a component (most likely a SLSB) that provides this functionality. As I tried to explain in the messages above, the former approach leads to maintenance headaches and does not leverage J2EE's component nature. I think we agree on that.
Gal's concern with specific pattern wording is understandable. His correction summarizes the discussion very well. My original statement remains true, however. Its only focus is pattern’s benefits, which concentrate on the development aspects of an MDB. By the virtue of business logic functionality being provided by another component, MDBs become easier to implement and development process more efficient since you do not need to embed business logic into the MDB implementation. On the other hand, Gal’s wording captures the spirit of the pattern and the whole discussion a little bit better.
Well, we seem to have converged to a more finely-tuned form of the pattern we all agree on. I guess nobody is gonna have the patience to read through this long discussion (even though Leo's posts were very interesting, IMHO).
Too bad TSS doesn't let the pattern author edit the pattern description or summarize his views after the community had discussed the pattern... Floyd?... :)
Bruce is on the right track here. Implement business logic in java classes. Use MDB and SLSB to proxy calls to the business logic classes. Makes unit testing easier as well.
As I pointed out before, implementing business logic in Java classes compromises your application's maintainability and breaks the concept of component architecture. Even if you utilize MDBs and SLSBs as proxy mechanisms to your business logic classes, your code base is greatly affected by the changes to them. In a circumstance when a business logic class’s implementation has changed, all of the MDBs and SLSBs relying on this class most likely will need to be recompiled and redeployed. This does not constitute a true functionality sharing but rather code sharing reminiscent of C++ programs of 10 years ago. When business logic is represented as a component (SLSB), only major interface changes would force you to recompile and re-deploy all other Beans relying on it. Unit testing is not necessarily harder with the component approach. All you need to ensure is that the class/program/client you are testing runs in a container context, so that it can access Beans in the same or other containers. This is not hard to do at all. Business logic components should be tested prior to sharing them.
a very good idea to post this pattern, indeed.
I used it when MDBs didn't exist yet; my team and I developped a similar layer on top of JMS so as to rapidly create "MDB-like" components within our J2EE Framework (the context was, you guessed, EJB 1.1).
So, I used this pattern when I had to design a business statistics architecture for our portal. Every stats were handled through a (synchronous) layer made of SLSB, Façade: a "Stat Manager" layer.
The point/idea was to produce business statistics events without disturbing our business services. Here came this pattern to the rescue. We created an "Asynchronous Stat Producer" inherited from the "MDB-like" component and made it call the "Stat Manager" to actually produce stat events.
This way we made the entire application totally independant from the "statistic sub-system", which is a good OO principle too.
Message for Leo,
I agree on all of your arguments. However, I think that Gal has also made his point. I think it is quite common to use java classes for implementing business logic which in turn is called by beans.
You can take minimize the effect of "In a circumstance when a business logic class’s implementation has changed" by defining a common interface that can be implemented by SLSB and the business logic class.
This is a response to Arjuna's message.
I don't disagree that implementing business logic through Java classes is a workable approach. It, however, comes with certain drawbacks that I tried to emphasize in my earlier posts. The main issue is code vs functionality sharing. In the component architecture, you want to reuse the functionality that has already been built without having your application rely on its actual implementation. Aside from the obvious sharing across multiple applications aspects, the components break the dependence of a system on a particular set of code. They provide a collection of well-defined services that can be utilized by other applications and components. The whole beauty of component architecture is that changes and enhancements to specific components can be made transparent from their clients. By ignoring these benefits and using classes instead of components, you are, in essence, discarding what J2EE has been created to achieve.
One of my favorite Eckel quites seems to capture this, "50% of the art of OO design is in understanding that every design problem can be solved by adding a new level of indirection. The other half is in knowing when to stop indirecting".
Well, I think you hit it on the nail a while ago Gal. All we're trying to do here is reuse code. Is there such a pattern?
This pattern suits well when we need to process asynchronous client requests, for example in a loose coupling fashion. I think to implement this pattern for a workflow manager, named ProcessManager, and in fact this is a MDB. Theres a similiar or even equal pattern proposed by Gregor Hohpe et al, in his Enterprise Integration Patterns book. What we need to think about it, it's that we could extend beyond this pattern, and integrate it in a service-oriented architecture. In fact we could create a WebServices Proxy, where we don't need to build a wsdl for each webservice and bother with all preprocessing or matching code, and to build a WebService Proxy or Listener that catchs client's requests, dispatching messagges to specific business classes (stateless facades).
I disagree with the premise that all the business logic should be located in Stateless Session Beans. If MDB are a type of SLSB, then why not have business logic in them instead of introducing more indirection? Does it really matter how they are invoked? A JMS message or a remote invocation are not really *that* different.
My MDBs tend to implement batch processes, some of which can take hours or even days to run. The whole point is to avoid synchronous calls, and delegating to a SLSB re-introduces the synchronous problem again (to a lesser extent as it is within the container now).
IMHO, MDB logic is often fundamentally different to SLSB logic - that is why it is triggered asynchronously, because it can take a considerable time to process. If their logic can simply be delegated to a SLSB, perhaps MDBs shouldn't be involved at all.
This is a response to Bruce Blackshaw’s message.
As any other pattern, this one should be used judiciously. If conditions are such that it is not applicable or would cause significant performance issues, it should not be implemented. Please read my response to the previous message that discusses the concept of shareable business logic. It underscores the idea of promoting reuse across components and applications by encapsulating business logic in SLSBs. In this situation, it is relevant where business logic is located. If it is limited to the MDB only, it becomes inaccessible to all but JMS clients. If, however, business logic is implemented in the SLSB layer, it can be accessed by a variety of clients, one of which is MDB.
On the other hand, if functionality needed to be performed by the MDB is truly unique and does not need to be reused by any other component, this pattern would become unsuitable.