One big Service class or several small classes?

I asked this question on the Spring Forums a couple of days ago, but I didn't get a response, so I'll try it here.

Looking through the Spring's petclinic and jpestore applications - both seem to advocate one single class to interface with the database (hibernate or dao/ibatis). I also noticed this pattern in Java Open Source Programming. Is this a recommended pattern or do you still think there's value in several service-level classes (i.e. one for each DAO)? I imagine the single interface and impl could grow quite large on a big project. In fact, in the Spring examples, the Manager isn't even a Business Delegate, it's really a Persistence Manager.

BTW, the Petclinic app is a helluva plug for Hibernate. The Hibernate implementation class is 53 lines, and the JDBC implementation class is 770 lines! If you're still using JDBC over Hibernate, please explain why you put yourself through the pain?

I do like the simplicity of the Single Manager approach, but I tend to do 1 Manager for each DAO (or something resembling this pattern). What do you advocate? AppFuse follows the 1-Manager to 1-DAO pattern. Should I switch to the Single Manager pattern?

Posted in Java at Feb 02 2004, 09:44:39 AM MST 19 Comments
Comments:

The petclinic example is trivial enough that a single DAO is probably ok. Personally, I think one entity=one DAO right now because I'm converting from EJB's. A single manager will quickly become too large to be easily maintanable.

Posted by Sam Newman on February 02, 2004 at 11:46 AM MST #

I tend to use a hybrid approach for Hibernate interaction. I define a HibernateHelper class that wraps the Hibernate configuration and details - it configures the session factories and assists with session and transaction management, and also has some simple utility methods such as store(Object) that handles the simple situation where you want to store a single object with no extended transaction. Obviously, this class must know about all persistable classes so it can properly configure the session factory. Then, each class has its own XXXXService that contains all the find() store() etc methods for that class. Perhaps not perfect, but it's proven to be fairly flexible and managable.

Posted by Rob Kischuk on February 02, 2004 at 12:12 PM MST #

For largish J2EE projects we use separate classes for the the service layer, and a separate persistence layer underneath that for the domain objects. The services evolve independently of each other, and are the only interface for the web layer to call downward.

Posted by B. K. Oxley (binkley) on February 02, 2004 at 01:18 PM MST #

We use service classes on different levels (a layered service layer ;)). But seriously, those service classes that sit closest to the front-end we call 'use-case drivers'. They encapsulate business logic (incl. database access, mail/messaging, etc.) that is required by a particular piece of functionality like user maintenance. Lower levels services are used to encapuslate 'system services' like emailing, messaging, conversion to PDF, etc. If a DAO get's complicated enough (larger object graphs) it might warrant its own service/manager class. Note: we do use common sense though, layers are not a must have, but in our relatively large app, the use case driven services vs. the system services works quite nicely.

Posted by Jaap on February 02, 2004 at 02:34 PM MST #

So, to answer your question: I don't think you should follow a rule of tumb saying 1-to-1 or 1-to-all. Look at your use cases. I think AppFuse has 2 main ones: manage users & upload file), so 2 managers should suffice.

Posted by Jaap on February 02, 2004 at 02:45 PM MST #

But what if it comes to complex SQL joins? Let's say you have a manager/service SA that handles objects of type A and onther manager SB for objects of type B. Now if objects of type A hold an reference to and object of type B you may either give service SA a reference to SB or let SA access tables of SB via a SQL join directly. The latter solution is ugly from the OO point of view for it circumvents encapsulation. On the other hand the join would perform much better since the aggregation happens at the database layer. To clarify my problem here's a simple example: Let's say we have News N, articles A and comments C. Both news and articles should be commentable.

Solution 1: A news service and an articles service where each service provides support for comments, e.g. each service might have something like addComment(...). The drawback of this solution is that code duplication is required though it might be prevented by using inheritance.

Solution 2: A news service, an articles service and a comments service. This approach seems to follow the composition idea and allows other entities to be commentable as well (for example comments service might have a method like addComment(String id...)). The question is if news service and articles service should use comments service directly or should the consumer of the services ask the comments service for comments of a given entity? Furthermore this solution requires more SQL selects.

All in all I think the main problem is that SQL isn't object oriented. Though you have tables which could be seen as classes joins of two or more tables produce new types (or combinations). Even a select <column> from <table> produces a new type.

Posted by WoEyE on February 03, 2004 at 07:18 AM MST #

WoEyE, your right, SQL isn't Object Oriented - hence the use of Object-relational mapping API's. When I go to my Data Persistence layer I ask for an object, and let the layer do the mapping. Lets take a simple example - imagine I go to my persistence layer and ask for a Record Collection. Internall the database might represent this as a RecordCollection table and a Record table. Now with JDBC in order to create my bean we would require an explicitly coded join. However if I used Hibernate the join is hidden as I've setup the relationships. My Service layer tends to be use-case driven (task oriented) rather than entity driven - a user-request calls one method on the service layer. Complex joins will be a big problem if you take an entity-focused approach (e.g. A RecordManager for records, a Collection manager for RecordCollections etc).

Posted by Sam Newman on February 03, 2004 at 07:26 AM MST #

Sam,

WoEye's use-case still represents an interesting example. I wonder how you would approach this using a use-case driven service-layer. I have a similar example, with BlogEntry and Album entities that both can have a one-to-many relation with Picture. Currently, both the BlogEntry and the Album hibernate mappings know how to fetch their respective Picture-collections. Difficult to separate this into a separate PictureService, but perhaps in such cases code-duplication is wiser?

Posted by Jaap on February 03, 2004 at 08:45 AM MST #

Sam, nice point! The thing is that Hibernate already serves the entity level and gives us enough flexibility to handle even complex joins. I like the idea of services which are more use-case driven. Butt consumers of such services would need how to fetch an entity from Hibernate. And as a side-effect they would need some style of SQL queries (Hibernate SQL). I am not sure if Hibernate as a layer is abstract enough in order to keep, let's say Struts actions, maintainable. What happens if you change a table and the mapping file? How do you find the dependent actions? I guess the main problem here is that Hibernate SQL is loosely coupled whereas a method invocation of a service bean is strongly coupled and can be easily found or refactored.
Another point I want to bring up is the question whether a join for result lists returned by Hibernate should be considered as a class? Nowadays I tend to handle them as maps and not as classes for there are typically read-only (or should I say display-only). And of course it gets quickly very time consuming to write classes for each join combination :-)

Posted by WoEyE on February 03, 2004 at 10:37 AM MST #

It's all about your application requirements. If the number of business operations is fairly small and somewhat closely related, a single business facade is appropriate IMO. Petclinic and JPetStore are still different here: Petclinic has a single business object that is essentially just a DAO - the requirements are simple enough. JPetStore has a single business facade but delegates to several DAOs underneath - its persistence requirements are more demanding. Juergen

Posted by Juergen Hoeller on February 03, 2004 at 11:10 AM MST #

Something everyone has overlooked is the possibility of automatically generating much of the service layer via XDoclet or a similiar tool. All of my persistable classes implement the empty AutogenerateDAO interface. This tags them so an XDoclet task will create a DAO & hibernate implementation with the standard CRUD functionality. I haven't needed additional functionality yet, but there are hooks for it in the standard XDoclet inclusion mechanism.

The only remaining piece is a common base class for the Hibernate implementation, allowing the XDoclet-generated implementation to be very thin.

This gives me the benefits of a 1 class - 1 DAO design but in practice I only need to maintain two files - the base Hibernate implementation and the XDoclet template. I can add a new method to all DAOs by changing a single template file, or change the persistence layer from Hibernate to something else by providing a new base class and making suitable changes to the template file.

Posted by Bear Giles on February 03, 2004 at 11:37 AM MST #

I agree that <em>it's all about the application requirements</em>, so for true "sample apps" - it's definitely appropriate that there is one business facade.

The problem is that developers might use these sample apps to develop their own apps - borrowing the patterns, copying code, etc. I know that I've done this and continue to do it when I see patterns I like. Maybe there just needs to be more visibility in the documentation saying <strong>a single business facade works great in small applications, larger applications may require several</strong>.

Thanks to everyone for the feedback - good stuff.

Posted by Matt Raible on February 03, 2004 at 12:43 PM MST #

The challenge is that of vertical partitioning. Or partitioning vertical slices of business functions in to logical groupings. In my experience this is more difficult, and just as (more?) important than the typical horizontal software layering (presentation, domain, persistence etc.). Frameworks take care of the latter for us, but nothing will vertically partition our applications automatically (or smartly) because every application is very different. I don't agree with generating the service layer, because that's you're business you're messing with! It should be partitioned with some thought.

JPetStore was a simple app that needed just one simple service. Most business applications will require a number of these. It's a good idea to spend some time thinking about these services and how they relate to each other (in terms of the business) and to the horizontal layers as well (in terms of the architecture). I'm also a fan of separating "nouns" from "verbs", and therefore in JPetStore you'll notice that I have a separate group of domain objects that the service layer will act upon. This is a much more scalable architecture and can easily be ported to Session EJBs at a later time if you want to (services Business Deligate pattern, domain objects become DTOs).

If you're application generally ends up being 5 layers and 10 vertical partitions that line up perfectly like a grid (one action, to one domain object, to one service, to one DAO to one database table...etc.), then the solution is probably over-architected. Services and domain objects should partition logically to the business, which does not always (rarely) matches up to the database(s). Similarly, the user intercace is often some bastardization of the domain model and business process such that even it doesn't line up to the services well. The point of layering is that none of this misalignment is a problem. The layers deal with the misalignment. If you're not misaligned, then too many layers just becomes extra work. Beware if you groan every time you need to code "yet another layer", or if you're thinking: "Man, I should just generate this here service layer!" If this sounds familiar, you might not need to layer as much as you are. There is a such thing as overdoing it <GASP!>. :-)

Cheers,
Clinton

Posted by Unknown on February 04, 2004 at 12:30 AM MST #

I like Anonymous Cowards comments about over possibility to over layer and difficulties with auto-generated service layers.

Posted by anonymous too on February 04, 2004 at 05:12 AM MST #

Man, I'm offended. :-) I'm not an Anonymous Coward! I'm Lazy!

Posted by Clinton Begin on February 04, 2004 at 09:07 AM MST #

I'm using 1 DAO per business entity, but most of the hibernate usage is that same:

save(), update() saveOrUpdate(), load(), delete() etc.

Most of my DAOs just provide useful queries: findEntityByName(String name) etc.

And as an aside, thank the lord for the Spring framework. Never before has programming been SO simple.

Posted by Ian Blizard on February 06, 2004 at 10:59 AM MST #

Hi , would like some clarity on the organization of the business services layer. i see 2 approaches: 1. role based Biz svcs interface facades hidden behind feature based groupings of biz svcs so that UI has simple task of talking to a single interface of that role and keeps communication between UI and biz svcs simple. cons:its is staticly defined, does not scale when the roles (and what thesse roles can do) change. works good when the roles are static and do not change and are well defined during the design. second approach: remove the role based facades and expose the feature based biz svcs directly to UI, may make UI a little complicated but scales to dynamic roles. question here: how coarse grained/fine grained these feature based biz svcs can be. any pros/cons of the 2 approaches and wud like some view on granularity of organization of biz svcs thanks Bhupesh

Posted by Bhupesh Arya on March 01, 2004 at 02:57 AM MST #

I think I agree with Clinton. Layers should be not aligned. Make a service layer use more than one data layer. And I think, an interface should define what exceptions it may throw. Because the layer that consume the lower layer, will have no idea what kind of implementatation currently used.


But then when we still care about compatibilty with jdbc data layer (not only ORM data layer compatible), we should create service implementation that treat all dao as jdbc dao. I mean, we should not leverage data graph (student.getFather().getName() in ORM since jdbc implementation can't do it and service implementations do not care about data layer implementation.

Posted by Muhammad Ichsan on June 22, 2006 at 03:59 AM MDT #

We are using one Dao and service per entity.

Posted by Sudhir Nimavat on January 12, 2007 at 05:11 AM MST #

Post a Comment:
  • HTML Syntax: Allowed