Opinion: One big Service class or several small classes?

Discussions

News: Opinion: One big Service class or several small classes?

  1. Matt Raible has opened up a discussion on designing your service classes, specifically persistence services. He talks about the en-vogue approach "Single Manager" versus 1 Manager for each DAO or the like.

    Excerpt

    "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."

    Read Matt Raible in One big Service class or several small classes?
  2. 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. Of course, this does by no means result from some Spring limitation: Spring allows you to have *any* number of business objects, DAOs, resources, whatever - defined in any number of context definition files.

    Note that Spring's Petclinic and JPetStore sample apps are different in that respect: 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. So effectively, JPetStore's facade *is* a delegate.

    I personally tend to have multiple business objects (managers) in my applications, though not necessarily one per DAO: My business facades tend to be more coarse-grained than my DAOs. In general, DAO granularity is an interesting point: Transparent persistence tools like Hibernate allow you to have quite coarse-grained DAOs that just cover primary persistent objects (cascading to the rest); no need for CRUD operations for each nested object.

    Juergen
  3. I like to group related use cases into delegates/facades as well. These usually represent an interface to more finely-grained service managers (session beans, etc.), though not on a 1-1 basis unless the requirements for a project will remain reasonably static.
  4. Hey Matt,

    My team is just completing a large j2ee Struts/Hibernate/WS project. Some DAOs access Oracle through Hibernate, others access a web service, and some others access property files.

    We have around 50 DAOs in the application. We've created about 20 facades which use various DAO compositions to provide business objects to the business logic layer. One reason for having so many facades is that in a project with 10+ developers working concurrently on the same codebase it was easier not to step on each others toes by creating feature specific facades.

    So we had one facade for each distinct feature of the application, each feature being related to one or a few related use cases and design packets.

    This setup worked very well for us.

    Regards,

    Pascal Forget
    Pascal.Forget@cgey.com
  5. I generally create service interfaces (and hard impls) which address one use case or a set of related use cases. These service objects do their work via other service objects, and also via a number of Mapper/DAO objects (defined as interfaces with hard impls. for the like of Hibernate). Because I am assuming that this code will always live an O/R mapping environment (ie Hibernate, TopLink, JDO 2, etc.), I have only one root Mapper interface that can do basic load/save/update operations on _any_ domain object. I then have specialized Mapper/DAO interfaces for some of the domain objects, which contain for the most part only special finder methods, or more complex loading scenarios where I need to override the default fetching of things like lazy loaded relationships.

    This is usually deployed inside a Spring ApplicationContext, with the context providing a declarative transactional and hibernate wrapper around the service objects, and those then being used automatically by all the contained code.

    This has worked out pretty well in practice.

    Regards,
    Colin
  6. I generally create service interfaces (and hard impls) which address one use case or a set of related use cases

    This is exactly what we usually do. A fine-grained structure has its advantages when having more than a couple of use cases that might not even be related - just as Juergen pointed out. You can easily refrain a certain role from not accessing a certain manager (and thus a certain set of use cases), while other roles may access it. I think that's harder (at least to maintain) when defining all operation on one service class.
  7. I generally create service interfaces (and hard impls) which address one use case or a set of related use cases

    Yep, that is what I do, too.
    But I put an extra layer of flexibility (IMO) to the stack of layers. Closest to my web tier components (mostly Struts actions) I have a plugin layer...Here different plugins are located. Plugins are loaded in a plugin context based on some criteria..say user role for example.
    A plugin has an interface, you could say that this is thh "service interface", bacause plugins are just another layer of services..but a flexible and dynamic one.
    A Plugin supports versioning, so that I can "load" different versions of the same plugin interface..ie load a specific implementation of the plugin interface.
    A Plugin connects to a service class, say a Session Facade, DAO, whatever and the service class connects to persistence managers or handle the persistence by it self.
    So to summarize I usually have the following flow
    Struts action->Plugin object->Business service->[Persistence service]

    I have found this approach extremely flexible, because I can control which version of the plugin object to load, based on a certain criteria. Core business services to the end user is still the same as the plugin interface is still the same....implementation differs though

    Just my few cents...Any opinions...Overkill?

    Best regards
    Henrik
  8. I generally create service interfaces (and hard impls) which address one use case or a set of related use cases

    This is exactly what we usually do. A fine-grained structure has its advantages when having more than a couple of use cases that might not even be related - just as Juergen pointed out. You can easily refrain a certain role from not accessing a certain manager (and thus a certain set of use cases), while other roles may access it. I think that's harder (at least to maintain) when defining all operation on one service class.
  9. Layered Services[ Go to top ]

    I find it can be useful to layer services. The front layer contains classes corresponding to use cases, the back layer contains classes which co-ordinate persistence, messaging, any other external interfaces.

    In this way, the front layer provides a service to client applications, while the back layer provides a service to the front layer. It can be overkill in some circumstances, but it's certainly useful for complex interactive applications.

    The font-end services are definitely of the "Single Manager" ilk, back-end are a mix depending on what sort of interface suits the front-end.

    I do think "1 Manager per thing" classes, while often valid, should be viewed with a skeptical eye. In some circumstances, they indicate someone has failed to see an elegant way to abstract away common features. Or maybe a bunch of classes has just evolved without consolidation. For instance, some applications contain a smattering of distributed singletons, all with the same boilerplate "static instance" code (effectively a managing code snippet for each singleton class). These could easily be replaced by a "Single Manager" factory (even in the distant past pre-dating third-party dependency injection frameworks). Likewise for cut-and-paste DAOs, and entity-managing session beans which pass requests through to entity beans.
  10. Layered Services[ Go to top ]

    I like to hedge my bets with this kind of thing. I usually define a "default" or generic service, with some kind of customization logic allowing me to replace it with a class-specific service where necessary. I use the target class as a parameter for the factory method of the service:

    ServiceForAClass ServiceForAClass.getInstance(Class targetClass);

    The factory method:

    a) Check first if there is a custom service for that class, returning it if it exists.

    b) Returns the generic service if there is no custom service. I usually initialize the generic service using reflection.

    Most of the time I find that the generic service solves all my needs, but on occasions, the custom service is a total life-saver.

    I tend to define the custom services either in some sort of config file (grouped by class), or using some class name conventions. For example, I might name custom DAO as follows:

    [package].dao.[class-name]DAO

    If this class does not exist, as determined by Class.forName(), the system reverts to the generic DAO.

    As a final note, I strive to make all services stateless, thread-safe singletons, to reduce memory overhead and ensure that each service object is only initialized once.
  11. 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
  12. 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.

    the data access service layer is a logical view of data intensive application, I see no good ways to generate it. It can be very simple and generic like "PersistenceManager" and can take class as parameter, but most applications I have so use specific version of "PersistenceManager" per class.
    This is very boring (delegation for generic "PersistenceManager"), implementation can be generated from interface declaration.
    It is not very popular, but I prefer this way:
     http://voruta.sourceforge.net