Announcement Announcement Module
Collapse
No announcement yet.
Architectural Considerations: Spring, Hibernate and the Domain Model Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Architectural Considerations: Spring, Hibernate and the Domain Model

    Hello,

    I am working on the overall design of a brand new system, so no legacy issues are relevant, and we have decided on Spring and Hibernate. I have found lots of useful information here and elsewhere, but what I am missing is the notion of what the best practices are for this type of system in 2008, based on everyone’s experience.

    So looking top down I am guessing we would have something like this:

    Web Layer
    API / Service Layer / Domain Objects
    DAO Layer
    ORM objects
    Hibernate

    Here is my take at what these layers do, along with some questions...

    Web Layer - Does web stuff. Duh.

    API / Service Layer - Implements business logic. This is what is exposed to all external consumers of the system (there will be others besides the web layer).

    Domain layer - Models the business problem. These interfaces, along with the API layer will also be exposed to the outside word (web, etc), but nothing else will be.

    DAO Layer and ORM objects - Interacts with the DB via hibernate.

    ?? OK so here is where things get fuzzy. The API layer will use the DAO layer to get info from the DB (Spring can inject DAO impls), but what should the DAO layer return to the API layer…. full domain objects (which I consider part of the API), or the ORM objects that Hibernate generates with its DB mapping?

    I think the way the ORM objects are created has a big effect on all this. If we assume that we manage hibernate mappings in an XML file (one per table/ORM object), then hibernate will GENERATE the ORM objects for us. This is fine, but then we are not going to add any intelligence to these ORM objects, they are going to be simple getter/setter bean things just for talking to hibernate. So are we going to have all this code creating meaningful Domain objects (which may be a bit less anemic) out of the ORM objects?

    ?? If we were able to auto-generate the hibernate XML (and SQL) from annotated ORM objects, then since we are building the ORM objects, maybe we can put some more intelligence into them? But then can the annotated ORM objects become something more, like a Domain object (or an implementation of one)? Or do we just annotate the Domain objects and not have an ORM layer at all?

    I guess it all comes down to this…. how to the hibernate ORM objects fit into this architecture? Are they totally hidden from the API via the DAO? Can we forgo these objects an use annotations on our Domain objects?

    The OO purest in me feels that everything should be totally separate. The Domain / API is the model of our problem. That should be developed independently from Hibernate or Spring. This is what we used to do 10 years ago right? My only problem with all this is it seems like object/class bloat. We could have DTOs converting ORM objects backed by XML into Domain/API objects. Then Service/API objects performing business logic on them. Then Web layer code converting Domain/API objects into View/Value type data objects for the GUI (json for example). Add in the fact that you have interfaces and impl classes everywhere at it seems like you need ten million classes for a simple round trip from the UI to the DB (Web VO / Domain / Service-API / DTO / ORM object). And if these things are somewhat similar we have all this code setting and getting stuff from one layer to another. Can’t there be just one thing we pass around?

    Is there some way to make the Domain/API objects both smart enough for business logic, but dumb enough (or maybe smart enough depending on your point of view) to be used by the Hibernate APIs / DTOs for persistence?
    And if we are only using Hibernate, and will be for the foreseeable future, do I really need the DTO layer? Can my API just talk to hibernate via the ORM classes? I mean common… I am stating to see why people like ruby these days

    This whole discussion also bleeds to the anemic domain model anti-pattern battle as well. I think I want a somewhat smart Domain model that uses inheritance and all that OO stuff. But if these domain model objects also double as hibernate mapper things maybe that breaks down?

  • dejanp
    replied
    I have started a new system as well. And have started down the road of exposing my Business Model objects which are hibernate persistance objects to my service layer. The problem I see is whe you have lazy initialization. It just does not seem right to me that the service layer has an object which has an attribute called say itemList which is a set of Item objects. However, since the itemList is lazily initialized it can not access it without opening a transaction/session. It seems that your data access layer now creeps into your service layer, and your service layer now has to know that something is lazily initialized or eagerly fetched. I am at a cross roads now as to best implementation.
    Sorry, forgot to quote.

    Leave a comment:


  • al0
    replied
    I guess here is some misunderstanding - I do not see any post here that objects to making service transactional.

    Originally posted by dejanp View Post
    I must admit i don't really understand what speaks against making services transactional and use simple "required" (default) transaction propagation.

    Service methods are in most cases represent units of work or have to be used as a part of larger unit of work and that's exactly what you can achieve by making them transactional.

    Leave a comment:


  • dejanp
    replied
    I must admit i don't really understand what speaks against making services transactional and use simple "required" (default) transaction propagation.

    Service methods are in most cases represent units of work or have to be used as a part of larger unit of work and that's exactly what you can achieve by making them transactional.

    Leave a comment:


  • al0
    replied
    Originally posted by stbill79 View Post
    I just had the same problem with one of my projects:

    1. Get the book Patterns of Enterprise App Dev. By Martin Fowler - it goes through most of this stuff pretty thoroughly.
    Really hate this book (as well as the most of Martin Fowler stuff). "What is true is not new, what is new is not true" But I should agree that to big extent it is matter of personal taste.

    2. Don't let the web layer access the domain objects much. Since the web layer is basically dealing with converting get/post string parameters into lookup ids ...
    This is only one if the possible approachs to Web-layer, depending on used tools/frameworks approaches may radically differ. It may be so (depending on used tools, that web-layer need not to know about get/post parameters (and even about very existence of get/post methods). One such example are ThinWire-based applications.

    5. The service layer interacts with a DAO interface, not an implementation. Thus the service layer should never touch the hibernate api, including methods like flush, merge, etc. Imagine you decide that Hibernate is not working out alright, and you want to switch to the next greatest ORM api or even something completely different. If your service layer is using the Hibernate API anywhere, then you now have to change your service layer along with your DAO layer. BAD NEWS.
    Such methods as flush() and merge() represent concepts with scope that far exceed Hibernate as such. By migration to ORM that need not them DAO implementation may provide empty implementation. And I hardly can imagine ORM that does not support these concepts at all.
    So providing such methods representing such concepts in DAO interface is not so bad.

    6. The dao layer could be controlled by anything - Hibernate, Toplink, or even pure JDBC, stored procedures, etc. Therefore, the service layer should never know about things like lazy initialization. If an Item lazy initialized a bunch of bids, the Dao interface shoudl have a method like getItem(Long itemId) and also getItemAndBids(Long ItemId).
    This way (especially if you have big and complex domain model) you may end up with very fat interface. And this interface would be very unstable against domain model evolution. Such approach is acceptable for small to medim-small application with limited time scope (significant model eveolution is unlikely). For bigger long-living application such approach may be fatal

    If the dao were to use JDBC, it would then have to load the Item first, then load each one of the Bid objects and initialize the item's bids into the item. Using Hibernate, it would just be easier for the DAO object to get the item and its bids in one HQL query which is like one line of code versus the many JDBC would require. That is where the Hibernate advantage comes into play, and not in the service layer.
    To force Hibernate (or other ORM) to work JDBC-style means delibirately loss the best of them. Sure when you ride a car you need start engine - you may to push car forward with pole throw window

    Basically the service layer should not do more than initialize objects from the database, then call on these domain objects to perform business logic. It is up to the service layer to know what exactly needs to be initialized in the domain model before call domain object methods. If an item does some business logic, and it expects that its bids are to initialized and in memory, it is up to the service layer to initialize an item and its bids. That is as far as the service layer goes into the business logic.
    As well serivice layer becomes unprotected against domain model evolution.
    Such approach may be unavoidable when JDBC (plain oir with stored procedures) is used, but compeltely defeats the very ORM purpose.
    I guess hard decision ORM (does not matter which) vs. JDBC have to be done. And only then service layer designed.

    And not forget that now most of the ORMs are available via unified JPA API.

    Regards,
    Oleksandr

    Leave a comment:


  • stbill79
    replied
    My Way

    I just had the same problem with one of my projects:

    1. Get the book Patterns of Enterprise App Dev. By Martin Fowler - it goes through most of this stuff pretty thoroughly.

    2. Don't let the web layer access the domain objects much. Since the web layer is basically dealing with converting get/post string parameters into lookup ids, it should basically just pass converted params to the service layer and receive fully initialized domain objects to pass off to the presentation layer. Do NOT let the presentation layer do more than just access getter methods of the objects along with some formatting (of dates, strings, numbers, etc).

    3. The service layer is where transactions are handled and also the business logic (basically calling on the initialized domain objects that are pulled out of the database).

    4. The domain objects (which are persisted using Hibernate should have rich behaviour - i.e. more than just getters and setters). Beware of having your service layer methods doing all the work that should actually be done by the domain objects.

    5. The service layer interacts with a DAO interface, not an implementation. Thus the service layer should never touch the hibernate api, including methods like flush, merge, etc. Imagine you decide that Hibernate is not working out alright, and you want to switch to the next greatest ORM api or even something completely different. If your service layer is using the Hibernate API anywhere, then you now have to change your service layer along with your DAO layer. BAD NEWS.

    6. The dao layer could be controlled by anything - Hibernate, Toplink, or even pure JDBC, stored procedures, etc. Therefore, the service layer should never know about things like lazy initialization. If an Item lazy initialized a bunch of bids, the Dao interface shoudl have a method like getItem(Long itemId) and also getItemAndBids(Long ItemId).

    If the dao were to use JDBC, it would then have to load the Item first, then load each one of the Bid objects and initialize the item's bids into the item. Using Hibernate, it would just be easier for the DAO object to get the item and its bids in one HQL query which is like one line of code versus the many JDBC would require. That is where the Hibernate advantage comes into play, and not in the service layer.

    Basically the service layer should not do more than initialize objects from the database, then call on these domain objects to perform business logic. It is up to the service layer to know what exactly needs to be initialized in the domain model before call domain object methods. If an item does some business logic, and it expects that its bids are to initialized and in memory, it is up to the service layer to initialize an item and its bids. That is as far as the service layer goes into the business logic.

    The domain logic should never know anything about how its associated objects are initialized. Remember - this is basically the whole point of the service layer.

    HTH - I'm only partially through the above referenced book, but I highly recommend it as Fowler has already gone through these problems and does a great job of going through the options in a very easy to understand manner!

    Bill

    Leave a comment:


  • al0
    replied
    I myself try to avoid DTOs as far as possible. In your specific case - which kind of control-related code do you plan to add to DTO? May you provide a sample?

    Regards,
    Oleksadr

    Originally posted by andrew_m View Post
    To DTO or not is a tough call. For us, the client does have requirements that are different enough, that I think it is worth the extra work. For example, in Flex you can bind controls like checkboxes to Java object attributes. So we are going to have code in the DTOs for specific thing like ui controls, and I don't think that belongs in the Domain.

    Leave a comment:


  • al0
    replied
    Originally posted by andrew_m View Post
    Our approach is beginning to take shape:

    - DTO and Web layer. DTOs are serialized and can be a subset of the full Domain objects. There will be a Web layer responsible for talking to clients and dealing with HttpServletReqeust, Cookies, etc. The Web layer will interact with the Service and Domain and assemble DTOs.
    It is not very clear to me what for DTO are needed in this place - unless you plan to have non-http clients.

    - Domain Layer. Rich, non-anemic fully OO Domain which models the problem and implements business logic. We are going to map the Domain to the DB with JPA annotations, or just use XML. This is still under debate.
    Both annotations and XML may be mixed and matched, as well XML may be used to override behavour specified by annotations. So I really do not see any reasons to debate

    But either way, we won't have any generated orm objects and maybe no XML.
    There is nothing wrong with ORM-generated objects as soon as they are generated once and used as starting point for developing full-fledged objects only.

    For transactions, I think we will use @Transactional. So if we go this route our service and domain will be heavily annotated. I don't think I have an opinion on that yet.
    There were discussion "Are annotated POJOs still POJOs?" in this forum not long go, may be it would be interesting to you (name may slightly differ).

    Leave a comment:


  • andrew_m
    replied
    Originally posted by al0 View Post
    Yes, it is major struggle of ORM - to make it transparent. Even not sure if it is reachable at all (in its more .

    Concerning forcing the initialization of all lazily loaded objects - it may be lesser evil then DTO. To fill-in DTO you anyway have to obtain data from database not lazily (I mean data to be put in DTO). And if DTO contains enough data for service layer and all data to populate DTO are already in the persistence object (otherwise they would not find their way to the DTO) then why not transfer persistence object in "populated" state?

    To DTO or not is a tough call. For us, the client does have requirements that are different enough, that I think it is worth the extra work. For example, in Flex you can bind controls like checkboxes to Java object attributes. So we are going to have code in the DTOs for specific thing like ui controls, and I don't think that belongs in the Domain.

    Leave a comment:


  • andrew_m
    replied
    Our approach is beginning to take shape:

    - DTO and Web layer. DTOs are serialized and can be a subset of the full Domain objects. There will be a Web layer responsible for talking to clients and dealing with HttpServletReqeust, Cookies, etc. The Web layer will interact with the Service and Domain and assemble DTOs.

    - Domain Layer. Rich, non-anemic fully OO Domain which models the problem and implements business logic. We are going to map the Domain to the DB with JPA annotations, or just use XML. This is still under debate. But either way, we won't have any generated orm objects and maybe no XML. So maybe these are "hibernate persisted domain objects" or something.

    - Service Layer. Services that creates, deletes or otherwise manage Domain objects. This is business logic that doesn't belong in Domain objects.

    - DAO Layer. These deal with the DB and create Domain objects.

    For transactions, I think we will use @Transactional. So if we go this route our service and domain will be heavily annotated. I don't think I have an opinion on that yet.

    Leave a comment:


  • al0
    replied
    Yes, it is major struggle of ORM - to make it transparent. Even not sure if it is reachable at all (in its more .

    Concerning forcing the initialization of all lazily loaded objects - it may be lesser evil then DTO. To fill-in DTO you anyway have to obtain data from database not lazily (I mean data to be put in DTO). And if DTO contains enough data for service layer and all data to populate DTO are already in the persistence object (otherwise they would not find their way to the DTO) then why not transfer persistence object in "populated" state?

    Ok, there is one more consideration, somewhat more pleasant - if service method handle transaction the it is in the scope of Hibernate session and so lazy initialization must succeed transparently.
    Later addition
    Especially, if service objects are (as it is typically) stateless.

    Regards,
    Oleksandr

    Originally posted by [email protected] View Post
    Believe me, I have no intention of forcing the initialization of all lazily loaded objects. What I am struggling with is whether to have a DTO/Value object transfer data between the data layer and service layer, or to just pass the hibernate domain object back to the service layer. I started out by passing the hibernate object back to the service layer, and that lead to hibernate specific or JPA specific code leaking in to the service layer. Now the service layer had to know to start a new session, refetch the object, and then access a lazily loaded attribute if it wanted to access the data. I don't believe that this type of logic should live in the service layer. It is ok for the service layer to control the over-riding transaction, but I am struggling to see how this type of persistance code belongs in the service layer.
    Last edited by al0; Apr 7th, 2008, 01:22 PM. Reason: Missed sentence

    Leave a comment:


  • rstacy@zcorum.com
    replied
    Believe me, I have no intention of forcing the initialization of all lazily loaded objects. What I am struggling with is whether to have a DTO/Value object transfer data between the data layer and service layer, or to just pass the hibernate domain object back to the service layer. I started out by passing the hibernate object back to the service layer, and that lead to hibernate specific or JPA specific code leaking in to the service layer. Now the service layer had to know to start a new session, refetch the object, and then access a lazily loaded attribute if it wanted to access the data. I don't believe that this type of logic should live in the service layer. It is ok for the service layer to control the over-riding transaction, but I am struggling to see how this type of persistance code belongs in the service layer.

    Leave a comment:


  • al0
    replied
    Nobody can prevent you from eager initialization, but ... You may end up fetching the whole object graph. But it may (and the most likely will) be simple too much.

    BTW, transactions naturally belongs to service layer (or even business layer in some cases) as only those layers knows which operations shal lbe atomic from business point of view.

    And note that inserting a DTO between DAO and service layer is not by any means better then "manual" forcing population of lazy-inited properties of your "Hibernate persistence objects". Anyway they need to be populated to transfer data from them to the DTO.

    Regards,
    Oleksandr

    Leave a comment:


  • rstacy@zcorum.com
    replied
    I have started a new system as well. And have started down the road of exposing my Business Model objects which are hibernate persistance objects to my service layer. The problem I see is whe you have lazy initialization. It just does not seem right to me that the service layer has an object which has an attribute called say itemList which is a set of Item objects. However, since the itemList is lazily initialized it can not access it without opening a transaction/session. It seems that your data access layer now creeps into your service layer, and your service layer now has to know that something is lazily initialized or eagerly fetched. I am at a cross roads now as to best implementation.

    Leave a comment:


  • al0
    replied
    Autogeneration is used relatively rarely and only early in the development phase to create object skeleton, to which you then can add any desired behavior.

    More typical approach is to create Java objects and only then mapping XML files or even avoid XML mapping completely and use annotations on your Java objects instead (note, it is possible to mix and match annotation and XML mappings if some Hibernate features are not available via annotations).



    Originally posted by andrew_m View Post
    I am pretty new to hibernate so I am still not getting something...

    From what I can tell, hibernate has these low level ORM objects that are auto-generated from XML mapping files. Since these are auto generated, I don't think we can give them that much behavior, so they won't likely be "smart enough" to be a true domain model. Am I missing something?

    Are you saying you maintain domain objects and annotate them for the DB mapping?

    Leave a comment:

Working...
X