I sometimes find people struggling with how to choose the right scope for your beans. While it can be confusing, I hope that this article will help with your decision making to figure out which scope best applies to your beans.
What's available?
As of Java EE 7, your normal scopes are RequestScoped, SessionScoped, ApplicationScoped, ConversationScoped and TransactionScoped. The first two are closely tied to the HTTP life cycle, typically when an HttpSession is started, you get the beginning of a Session Context available for use. When a session is destroyed that Session Context ends. Sessions are meant to be passivated and potentially serializable, so any SessionScoped objects you have must be serialization friendly, including their dependencies. A Request Context exists in many places, but the most straight forward use case is around the duration of a single HTTP Request. The various specs define a few other places where a Request Context is active - PostConstruct methods of Singleton EJBs, MDB invocations, etc.
A Conversation Context is started manually by a developer and is usually bounded within a shorter period than a containing context. For example, you could create a conversation as a child to a request, and close it before the request is done. The typical use case is around a single session, a set of requests that are all meant to be done together in a coordinated fashion.
ApplicationScoped beans are created once, the first time the application calls upon them, and are not created again. Note that the scope of an application is somewhat ambiguous. If you have just a WAR, even if it has some libraries without EJBs, it will share a context. Anytime a JAR provides its own entry point (e.g. a JAX-RS endpoint, SOAP endpoint), that may be considered a separate application. EARs are known for introducing this kind of problem, as multiple WARs and EJB JARs will likely create their own unique contexts, resulting in multiple Application Contexts being available. When in doubt, if you can run a single WAR without EAR do it.
Transaction Scope was introduced in Java EE 7 as a part of JTA 1.2. This context is activated within the bounds of a single transaction.
There are two other scopes, pseudo scopes you could say, Dependent and Singleton. When working with CDI, Singleton is basically the same as ApplicationScoped, except that Singleton beans aren't eligible for interceptors, decorators. Dependent has similar restrictions, but has an interesting caveat. The injected bean shares its context with its injection point. If you inject it in a RequestScoped bean, then the injected Dependent bean shares the Request Context.
State & Data vs Services and Operations
As an application is doing work, it needs to maintain some amount of state. This could represent a user's session, entities being manipulated, a local cache of static resources. This is different than a service that may be performing operations.
Domain-Driven Design considerations
Suppose that you are designing a shopping cart, one of the timeless classics in software development. How would you model the ShoppingCart based on scopes, as well as building a rich model that supports the operations relevant to its use. Consider these interfaces
public interface ShoppingCart {
ShoppingCartItem addItem(Item item);
Order checkout(BillingInformation billingInformation);
}
At a very high level, consider these behaviors as well:
- A shopping cart is persistent. If I change it, and then leave the site, what I added should be available to me at a later point.
- Adding an item to my shopping cart is an atomic, synchronized, idempotent and request based transaction. I'm only able to add one item at a time in a single request but I could open multiple browser tabs and make changes in tandem.
- Since these operations are atomic and state is persistent, the changes I make should be automatically written to a data store as a part of these operations.
Based on this information, as well as the tips above, how would you scope these domain objects?
Here's one approach.
- Your ShoppingCart is session scoped. It's associated to a user, persistent but generally tied to a single user session.
- Your ShoppingCart is dependent on some kind of ShoppingCartService that is responsible for the persistence of the state of a shopping cart, and likely other services as needed (e.g. an OrderService for handling checkout w/ credit card information). These services are ApplicationScoped and operate on these classes.
- Item is a RequestScoped bean that represents what you're trying to add to your cart. It is built up in a single request and pushed into your cart.
- BillingInformation is a ConversationScoped bean, the data is built up in a few requests, and then calls checkout when all of the information is present.
- Neither Order nor ShoppingCartItem are managed beans. They are created within the scope of their respective methods.
These domain classes together represent the state of your application, specifically how things are changing over time. So how do you interact with them?
Controllers
If a controller is aware of the view and the backing model, what scope does it get? If you're dealing with JAX-RS, the answer is pretty easy - RequestScoped resource classes are mandated in a JAX-RS/CDI environment. You have a bit more leway with frameworks like JSF, but realistically a request scoped controller makes the most sense.
- It's bound to the HTTP lifecycle (assuming you're using HTTP)
- It represents the start of a request.
- It's the user's action of interacting with the system on a per request basis.
This doesn't mean you can't use a SessionScoped or TransactionScoped controller, but semantically it makes things clearer that it is request scoped. Another thing to point out, your request scoped controller can still interact with your session scoped model. Typically requests are made within a session, and thus give you access to the sessions contents as well as requet's contents. That means this controller is valid
@RequestScoped
public class ShoppingCartController {
@Inject
private ShoppingCart shoppingCart;
}
This is a perfectly valid thing to do.
Consider this alternate approach. What do you think is going to happen?
@RequestScoped
public class Item {
}
@SessionScoped
public class ShoppingCartController{
@Inject
private Item item;
}
In here, as mentioned above, Item is bound to a request, it represents the item that is being selected by the user. I marked my controller as session scoped since the lifecycle of the shopping cart is tied to a session. This is a legal injection and is threadsafe. What happens is that the context is bound to the HTTP request, the same controller (session scoped) will operate on two different requests. Obviously be careful for things like mutating the controller (since its a controller, shouldn't be an issue).
Services
Services, from my point of view have two valid scopes. First, they can be application scoped (or singleton, if you don't care about proxies/AOP) since they maintain no state. Second, they can be dependent since they also maintain no state, and can be reused in a wider variety of cases. I'm generally warry of recommending dependent scoped beans, just because of some ways they can be misused (e.g. not cleaning up, lookup/non-injection cases). As long as a service isn't maintaining any state, it can be used over and over again.
Conclusion
There you have it. I hope you found this article useful. Please feel free to add comments below.