Update: Pete Muir recently posted an alternative to the example described here for the UserProfile object. It avoids potential LazyInitializationException if you access a property which was not initialized inside the loading conversation since the UserProfile is SESSION scoped. I prefer Pete’s approach to the implementation but the concept is the same 😉
______________________________________________________________

One thing I have really come to appreciate in seam is the ask and ye shall receive approach of injection of not only components but also core domain objects (or concepts depending on how you look at it). One issue I’ve always struggled with is the requirement to constantly go and get some domain object that is used very often. This really reminds me of the EJB2 style lookups which was really why we starting using DI frameworks, right? Whether you look up the domain object through a DAO, through the session, or through some holder object, you still have to go and get what you need.

For example, let’s say we have a UserProfile object that is needed by most of our pages and many of the components in our application to retrieve user information. It may not always be needed (we have an Identity of course thanks to Seam which provides our authentication and authorization), but it is needed if we require personal information about our user.

In the general world of Java you could implement this in several ways but lets say we do so through something like the following:

public class UserInformationBean implements UserInformation
  ...
  private UserProfile userProfile;        

  // injected by your favorite DI framework
  // (Seam, EJB3, Spring, even PicoContainer)
  private UserProfileDAO userProfileDAO;
  ...
  public boolean isUserLocal()
  {
    UserProfile userProfile = this.getUserProfile();  

    if("Texas".equals(userProfile.getState())
        && "Dallas".equals(userProfile.getCity()))
    {
      return true;
    }        

    log.info("ya'll ain't from around here are ya");

    return false;
  }
  ...
  public UserProfile getUserProfile()
  {
    if(userProfile == null)
    {
      this.userProfile =  userProfileDAO.getUserProfile(
        SomeContext.getUserPrincipal().getName());
    }        

    return this.userProfile;
  }
  ...

Here is the associated dependency diagram for our UserInformationBean component:

This is all well and good but I would prefer if the call to retrieve the UserProfile was centralized since I use it often. This is quite easy with Seam:

@Name("userProfileManager")
public class UserProfileManager {
  ...
  @In private UserProfileDAO userProfileDAO;
  ...
  @Factory(value="userProfile", scope=ScopeType.SESSION,
                 autoCreate=true)
  public UserProfile initCurrentUserProfile()
  {
    return userProfileDAO.getUserProfile(
               Identity.instance().getUsername());
  }
  ...

Note that I scope my UserProfile to the session which makes sense since the user is not likely changing profiles (or we may be in for some legal issues) and we are using it throughout the application. Note that conversation scope should be considered if the object is only needed in a section of the application to ensure that our state is cleaned up appropriately. The use of other contexts will be discussed further shortly. So now my component looks like:

@Name("userInformation")
public class UserInformationBean implements UserInformation
  ...
  @In UserProfile userProfile;
  ...
  public boolean isUserLocal()
  {
    if("Texas".equals(userProfile.getState())
        && "Dallas".equals(userProfile.getCity()))
    {
      return true;
    }

    log.info("ya'll ain't from around here are ya");

    return false;
  }
  ...

Below is the dependency diagram for our UserInformationBean after the change. Note that our UserInformationBean knows nothing about the UserProfileDAO (nor does it care):

So you may ask what does this buy me? I still have to call the DAO to get what I need. Sure, you may have to call your DAO (or the EntityManager if your using EJB3), but the dependency on the DAO is in one place, not scattered. Your component is now only dependent on what it needs to get the job done and we’ve removed unnecessary layering thereby reducing coupling.

In addition, the factory method only executes once during the course of a user session as I’ve scoped my UserProfile to the session. Thus, the UserProfile is instantiated lazily (since the @Factory method only executes on demand) and if it is used elsewhere it is simply provided back from the context rather than invoking the DAO again.

I also find this approach much easier to test. Rather than having to provide a mock DAO (or even worse a mock EntityManager) to my component, I simply set the UserProfile to whatever I need it to be to perform my unit test:

...
public void testIsUserLocal()
{
  UserProfile = new UserProfile("Jacob Orshalick", "Dallas", "Texas");
  UserInformationBean bean = new UserInformationBean();        

  bean.setUserProfile(userProfile);        

  assertTrue("Howdy!  Ya'll oughta be returnin' true.",
    bean.isUserLocal());
}
...

Additional Considerations

One drawback to this approach is determining where these domain objects are coming from. It may not be immediately obvious as to how these objects are just appearing in the context for developers. One approach to help with this is using symbolic constants. Using our example above we could create a constant for the UserProfile:

...
public final static String USER_PROFILE = "userProfile";
...

We can use this in both the factory and the injection:

...
@Factory(value=DomainConstant.USER_PROFILE, scope=ScopeType.SESSION,
               autoCreate=true)
...
@In(value=DomainConstant.USER_PROFILE)
UserProfile userProfile;
...

In this way a user can easily seek out where USER_PROFILE is referenced rather than having to do a text search (which could easily result in a vast number of results especially if used in EL expressions and comments).

Another consideration here is how your domain object will be used. This is necessary to determine which context to outject to. Is my UserProfile being used heavily in all of the application or is it only a section of the application? Conversation context would be a better candidate if the UserProfile is heavily required in a certain section of the application. Also, what are the memory penalties involved? Would it be better to simply request when needed to avoid the additional memory allocation? This may be the case if the object is used infrequently. It may be best to inject a local instance only that is only used for the invocation. Developers should always carefully consider the context.

Bijection and Disinjection

One way to explicitly control the scope of the creation of the UserProfile would be to leave off the ScopeType in your @Factory definition. Because a POJO is by default EVENT scoped, the UserProfile would only be available to your component and you choose whether to outject it to a broader context. This can simply be accomplished through use of the @Out annotation.

If you choose to leave off the ScopeType and inject a local instance in your component, the @Factory method would be invoked once for each request that your component is invoked. Why is that you ask? Essentially, Seam disinjects any attributes in your component annotated with @In after the invocation. Disinject is really just a fancy way of saying set to null.

This is an important concept as you may expect your attribute to remain set in between requests if I scoped my component to the Conversation or the Session. This behavior makes sense though as Seam maintains contextual state. The current context state is always injected into your component for each invocation. Therefore, it makes no sense to maintain the attribute instance as it will be overwritten prior to the next invocation.

This type of domain concept injection is very useful as it helps to reduce coupling and the heavy layering constantly introduced into systems. There are many situations where this type of behavior is quite handy but it is only achievable due to the contextual injection that Seam provides.