Laziness, DTOs and the Open Session in View (Anti)Pattern

If you’ve built web applications using an ORM library like Hibernate, you’ve probably run into the “open session in view” (anti)pattern.  The online discussion of this swings back and forth, and I’m not here to comment on whether this is or is not a good idea.  But generally, all the posts on this topic take a few common considerations into account:

  • Is the database session kept open outside the service layer (and potentially into the view layer)?
  • Are domain objects re-used as data transfer objects (DTOs) or otherwise exposed beyond the controller?
  • Are dedicated DTO classes created, and if so, how is the transfer of domain data into the DTO managed/automated?
  • Are associations fetched eagerly or lazily, and if lazily, when/where/how are child entities manually fetched?

There may be no single right way to approach this, so it’s really just a matter of what your preferences are, and how you weight each of the trade offs in your design.  I’ve taken a certain approach in most of my past projects, which rests on the following answers to the preceding questions:

  • I prefer to terminate my database sessions at the boundary between the service layer and the MVC controllers.
  • I don’t have an objection to re-using domain entities as DTOs providing they are are outside the scope of a database session.
  • I generally prefer my associations to be lazy.
  • I assume controllers can have semantic awareness of the domain models they pass on to the view.

Given these statements, the approach I’ve taken has been to create service methods that accept one or more Fetcher objects.  A Fetcher is an implementation of an interface like this:

public interface Fetcher<a> {
	public void fetch(A entity);
}

Service methods look like this:

public A findByUsername(String username, Fetcher<A>... fetchers) {
	A user;
	user = domainDao.findByUsername(username);
	fetch(user, fetchers);
	return user;
}

Typically, service classes derive from a common base class providing the fetch method:

protected <A extends DomainEntity> void fetch(A entity, Fetcher<A>... fetchers) {
	for (Fetcher<A> fetcher : fetchers) {
		fetcher.fetch(entity);
	}
}

Here, DomainEntity is just the common base class of all entity classes in the domain model. Given this approach, a controller method can invoke service methods like this:

User user = userService.find(username, new Fetcher() {
	public void fetch(User user) {
		user.getFriends().size();
	}
});

So a couple things stand out here:

  • The controller understands that users have friends, and that the web request that is invoking this code wants the friends to be returned with the user.
  • The actual retrieval of the friends associated with this user is done by code defined in the controller, but executed in the service method.
  • The fetching of the friend entities is handled by the underlying ORM’s understanding of the relationships between entities that is already present in the ORM configuration.
  • The same service method can be used to retrieve different associated entities, by passing in a different Fetcher.
  • The domain entities passed outside the service layer remain mutable, however, changes will not be persisted to the database.

This approach has worked out well for me in most situations where the topics of DTOs, session scope, and lazy associations have been relevant.  It’s a good compromise between a layered architecture, a DRY data model, and a performant fetch strategy.

Next thing to take a look at is Hibernate fetch profiles and see if it serves the same purpose and is equally (or more) flexible for what I need.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: