I'm currently writing a medium sized web-application and find myself asking this question many times.
For example, let's say there are users, which have files (one-to-many). How should the UI access the data? One "extreme" would be to let the service layer "only" take care of the lazy loading, i.e.
public interface UserService { public User createUser(String loginName, String password); public Set<MediaFile> getFilesOfUser(User user); } or, on the other hand, never return entities, only their ids
public interface UserService { public long createUser(String loginName, String password); public Set<Long> getFilesOfUser(long userId); } This implies the service would need methods for querying (and setting) all "basic" attributes, too
public interface UserService { public long createUser(String loginName, String password); public Set<Long> getFilesOfUser(long userId); public String getNameOfUser(long userId); public void setNameOfUser(long userId, String name); } This adds quite a bit of code (and possibly additional load to the DB), but also protects from StaleData- or LazyInitializationException.
Subquestion: How - if using the first case - should updating, fetching etc. be implemented? Two methods come to mind
First, I could try to manually update the original reference (because things like collection proxies, version etc. only get updated on the returned object, not the original reference). This simplifies using those entities directly in the UI
public Set<MediaFile> getFilesOfUser(User user) { Set<MediaFile> files = user.getFiles(); if(!Hibernate.isInitialized(files)) { user.setFiles(userDao.findOne(user.getId()).getFiles()); // findOne to re-attach to the session return ImmutableSet.<MediaFile> copyOf(files); } but also
public Set<MediaFile> getFilesOfUser(User user) { return ImmutableSet.<MediaFile> copyOf(getUser(user.getId()).getFiles()); } The major difference in those comes to show when using versioning. In the former case, I have to update version etc. manually (user.setVersion(attachedUser.getVersion())) after every update, in the latter, a update call has to re-fetch the entity every time just to set the value:
public void update(User user) { User attachedUser = getUser(user.getId()); attachedUser.setName(user.getName()); attachedUser.setPasswordHash(user.getPasswordHash()); // etc etc userDao.save(attachedUser); } This does not seem very efficient / unintuitive.
Which is the correct way to go?
EDIT
Just had an other idea related to the "only return id's" method: Services could expose a public "get___ById(long id)" method, which then can be used to at least read the basic values of the entity.
Additionally, one could implement "get___ByIdEagerly(long id)" and return a initialized entity, but I think that's a bad idea.