Results 1 to 10 of 24

Thread: Merging service layer and DAO layer into single layer

Hybrid View

  1. #1
    Join Date
    Aug 2008
    Location
    Lodz, Poland
    Posts
    47

    Default Merging service layer and DAO layer into single layer

    I would like to here some opinion on following topic.

    I used to build my application with service layer and DAO layer - quite standard. Service layer contains interfaces (e.g. ProductService) and their implementations (e.g. ProductServiceImpl). It has dependency on DAO layer, consisting of interfaces (e.g. ProductDao) and implementations (e.g. ProductDaoImpl). This worked very well especially with JdbcTemplate-based DAOs, where the service sometimes had to call several DAOs for completing the task (call ProductDao, OrderDao, UserDao).
    With Spring 2.5 stereotypes I would mark all service implementation classes with @Service, and all Dao implementation classes with @Repository, which gives me DataAccessExceptions translation for Dao classes.

    However, now, when I'm usually using JPA-based persistence, I observe that in many cases the service classes simply pass all the calls to Dao, almost untouched. So I have the "saveProduct" method in service, which starts the transaction (by AOP) and simply calls the "saveProduct" method on Dao, which calls entityManager.persist(). This way separation of service and dao layer only complicates the life of developer, because two levels of inerfaces means more work to do to come to the source which really does the job, and service layer does not add any value.

    So the solution would be to throw away one layer. Which one? I saw that the samples of Spring Web Flow do not have the separate Dao level, only service layer. Service has EntityManager injected, and directly calls entityManager.persist() in such a case. I would say that the EntityManager plays in fact the role of DAO here. This sounds reasonable and makes the code cleaner.

    However, now I don't have the DataSourceExceptions translation, which was earlier done by @Repository adnotation on Dao layer. This is not crucial, but I like this DataSourceException abstraction offered by Spring. Can I combine it with this approach? I can mark my service layer with @Repository instead of @Service, but this is probably incorrect semantically. Or should I mark it with both @Service and @Repository?

    What's your opinion on this?

  2. #2
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    The Service APIs define the use cases the service is meant to implement. The DAO provides the specific implementation of data access. Obviously, these are different things. The point of separating the two is to keep the possibility open for changing/replacing the data access strategy/technology/logic -- if that becomes necessary -without affecting the application clients that use the service. (I understand that your question concerns the specific case when a service does little more than redirect to a DAO. In cases of some serious business logic, there are no options, really. Business and data access logic should be kept separate.)


    In your example, your Product service directly uses the UserDao, OrderDao, instead of accessing the user and order data through the User and Order services. That, probably implies that some Product API methods make a series of different DAO calls, e.g. to a User DAO method, and then to the Order DAO method. There is always a possibility that such kind of logic might change with changes in the DB schema, etc, while the use cases for each service remain the same. You don't want to change your service API each time the database schema changes, or when you come up with a more efficient logic to extract the data from multiple data domains (user, product, order, etc.) - for a single use case (single API call.) If you are using DAOs, your Product service should only be exposed to the User and Order APIs - that is, services - not the implementation details of their data access. A DAO, as the implementation detail of the particular service, must never be exposed to another service directly. Such things should always be hidden from the service's client, including another service. If you feel comfortable with hard-coding your data access logic and technology into you service once and for all, then, I guess, you can merge the two.

    These are all architectural decisions that must be made regardless of the frameworks and technologies you are using, so I don't think it should have anything to do with which Spring annotations you will use and how.

  3. #3
    Join Date
    Aug 2008
    Location
    Lodz, Poland
    Posts
    47

    Default

    Yes, you are right that in this example Product service should rather use User and Order services, instead of User/Order DAO. However, in such a case it is still possible that each service use directly EntityManager as the actual DAO implementation, instead of separate DAO-layer class written by a developer.

    In really complicated applications the separate service and DAO implementation can be beneficial, if both layers are complicated. But in the simple application (CRUD + some additional application specific logic) this creates only more classes with very little logic inside (say method "save" in DAO, which calls only entityManager.persist) - so no benefit in fact, as this can be done directly from service.

    What it has to do with Spring? As I said earlier, I like the translation of DataAccessExceptions provided by Spring. But if i remove the DAO layer, I would probably have to add this translation directly on the EntityManager method calls, which makes it a bit more complicated (though still possible, I think - I can inject EntityManager proxy - but I'm not sure if I can make it to work with Spring processing of @PersistenceContext annotations). On the other hand, if I decided to use JPA's EntityManager then perhaps it is simpler just to use the JPA exceptions directly in such a case.

  4. #4
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by grzegorzborkowski View Post
    Yes, you are right that in this example Product service should rather use User and Order services, instead of User/Order DAO. However, in such a case it is still possible that each service use directly EntityManager as the actual DAO implementation, instead of separate DAO-layer class written by a developer.

    In really complicated applications the separate service and DAO implementation can be beneficial, if both layers are complicated. But in the simple application (CRUD + some additional application specific logic) this creates only more classes with very little logic inside (say method "save" in DAO, which calls only entityManager.persist) - so no benefit in fact, as this can be done directly from service.

    What it has to do with Spring? As I said earlier, I like the translation of DataAccessExceptions provided by Spring. But if i remove the DAO layer, I would probably have to add this translation directly on the EntityManager method calls, which makes it a bit more complicated (though still possible, I think - I can inject EntityManager proxy - but I'm not sure if I can make it to work with Spring processing of @PersistenceContext annotations). On the other hand, if I decided to use JPA's EntityManager then perhaps it is simpler just to use the JPA exceptions directly in such a case.
    I totally understand your point. At the same time, I immediately see the flaws in your approach, to begin with. You assume from the start that your business logic is basically data access logic, and, moreover, that logic will be implemented using only one particular technology, e.g. JPA. Your architecture becomes data-access-driven architecture, not just architecture. More importantly, you see your middle tier as something that implements data access, not use cases! While it may work just fine for your specific case, I would strongly recommend against such mentality.

    Here's my point. On a very high level, you have a client application, and you have - potentially reusable - services that implement some body of work. What is a service?

    My definition: A service is a software component that implements a set of specific use cases in the given area of functionality. The service exposes these use cases through its public API, and abstracts everything else! The Domain Model for the area of functionality covered by the service is part of the service API. In other words, to the outside world, the service is defined by the use cases and domain model (entities it operates on.) A service may depend on other services that also expose their domain model and API. The domain model is shared between the service and the clients. One thing that should never, ever be exposed to the clients is any notion of data access, or any existence of a data store, for that matter. Let's say, you have some Order service, a User service, and a Product service. The order service may implement a use case called "Place Order". The API may look like this

    placeOrder(User user, product Product);

    or something of that kind... That's the only thing the client should know. Ever. If the service needs to call the Product service to check if the product is available, update the list of products, check some user data against the User service, etc. - all of that must be abstracted within this single use case API. The client doesn't care where the data is stored and whether it is retrieved from the data base, file system, web service, etc. All such things are strictly implementation details that must not be exposed.

    The Data Access logic is nothing but the implementation details of a service. Implementations may differ. DAOs are not database access objects, as many people, for some strange reason, believe. DAOs are meant to abstract any kind of data access and data source implementation: database, files, web services, any type of data resource. The fact that many people use DAOs to access only databases doesn't make the above statement false.

    This makes perfect sense. When you pull up a web page, you only are interested in the information that is displayed on the screen. You, as a user, don't care where it came from, which databases it is stored in. This is the most basic principle of software design: impose as little burden on the consumer as possible by abstracting as much complexity of the provider as possible, minimizing the knowledge requirements for the consumer. It is one thing for the client application to know that it can place an order by providing the product info and user info. It is just a basic abstract use case that will never change. It is completely different - and inappropriate - to force some additional notion of a data store on the clients by having them communicate with data access concepts directly.

    Based on all this, my approach is to always use the service layer as the generic use case abstraction. Within that abstraction, provide specific implementations of data access (if appropriate) - by injecting them, of course. Never expose the data access technology via the service API/domain model. (I know that the latter is extremely popular, unfortunately.)

    Theoretically, in your case, if your service use cases are nothing but CRUD operations, you could skip the DAO layer within the service and just code your data access logic within your service API. The bad thing about this, ofo course, is that it will be more difficult to swap the data access implementations if it becomes necessary. I think, even if your service APIs only act as simple pass-through methods to your DAO methods, it is a very small price to pay for ensuring flexibility and clean design.

    Any architect (or programmer) should always approach the MT design with a clear understanding of one basic thing: we are ultimately implementing Use Cases, not data access! Use cases are the real thing, data access is an implementation detail. So, always think Services (see the definition above), not DAOs, repositories, or other data access concepts...

    HTH,
    Constantine

  5. #5
    Join Date
    Mar 2009
    Location
    Brazil
    Posts
    25

    Default

    I think the definitions of service/repositories in Domain Driven Design can clarify many points in this discussion. You are focused on the technology (how to have DataSourceExceptions?), but services are much more than a layer to access the repository.

    I strongly suggest a read on Domain Driven Design Quickly and then a read on Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans.
    Regards
    Juliano

  6. #6
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by juk View Post
    You are focused on the technology (how to have DataSourceExceptions?),
    Wrong! i think the main point of this - and many other architectural discussions in this forums - is that the technology is just an implementation detail and should always be abstracted. This conversation was NOT focused on technology, but rather on how to abstract it behind the public API exposed by a service.

    but services are much more than a layer to access the repository.
    And where in the above discussion did you find any evidence of someone saying that the services' only purpose is to abstract the data source? Perhaps, you should have read the thread carefully first.

    I strongly suggest a read on Domain Driven Design Quickly and then a read on Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans.
    I think most of us - at least those who contributed to this thread - have read the DDD book. And, personally, I have never liked the idea of repositories promoted in that book. I think it is a bogus pattern. I am not the only one. So think Christian Bauer and Gavin King, the authors of Hibernate.
    Last edited by constv; Mar 11th, 2009 at 12:09 PM.

  7. #7
    Join Date
    Aug 2009
    Location
    DC Metro
    Posts
    2

    Default Solution to Service-Dao pass through methods

    I was searchig for a solution to exactly same problem. After a long time, came up with this solution utilizing dynamic proxy and spring FactoryBean interface. Find the solution at this link (i.e. my blog) http://ramesh-sahoo.blogspot.com/

  8. #8
    Join Date
    May 2010
    Posts
    1

    Thumbs up

    Very nice thread.

    Actually, i found it, as i was naively wondering if the use of JPA (instead of IBatis) had an impact on the good old architecture (presentation -> service -> DAO).
    Eventually, i was wrong doubting this architecture viability just because of an other data access implementation. That's what not also this thread tells me, but my common sense also at the end.
    We are often get so troubled facing with new techno, that it sometimes makes you confused and losing your beliefs. This thread has the advantage of replace things to their right place. To say, counting on his own experience and not dogmas, not focusing on technology mermaids but using it as a mean not an aim.

    Thanks for reminding me the cape, captain CONSTV !!

  9. #9
    Join Date
    Sep 2009
    Posts
    27

    Default What about transaction boundaries?

    Hi,

    I'm also struggling with the "proper" DDD-way of doing things. By proper I mean finding out how it fits into the rest of the world, rather than dogmatically applying everything I find in the net. I think we need to discuss more specific topics to gain a better understanding.

    I'm still half-way through the book and here's my take on things:

    My understanding is that DDD is all about your business tier. It doesn't mention much (at least as far as I've got) about other aspects such as the presentation/services tier.

    I'd expect that writing a DDD application is not so different in those regards, other than the fact that perhaps you've moved some (or a lot) of business logic into the domain and there's less in the services tier.

    So here's one concrete question: How do you handle transaction demarcation in DDD?

    Let's say you have methods on your domain objects that allow them to (agnostically) access persistence through repositories. Who do you allow to make such calls? Do you make the domain object @Transactional and allow anyone to use them (e.g. even the presentation tier)? After all, what's the point of having an order.save() method if the controller has to go through a service to use it? But isn't that a violation that pollutes the domain model with persistence implementation information? Ok, it's just an annotation, but the domain object suddenly becomes able to define a transaction boundary for some code path that ends up invoking a DB/JMS/... resource that it is unaware of.

    Coming from a "traditional" background, I favor clear separation and require that client code (e.g. presentation tier code) goes through some transaction boundary (e.g. a @Service in your services tier). The result of my approach was that after injecting "repositories" into my @Configurable domain objects, I couldn't get to them from my presentation tier unless I violated my principle of starting transactions at a clearly-defined boundary in my application... So I tried to keep the transaction boundary and realized that I must have taken a horribly wrong direction when I started writing stuff like:

    Code:
    Order order;
    // ...
    pubic void handleButtonPressInPresentationTier() {
         // order.processOrder(); <-- this would fail as it's not @Transactional
         orderService.submitOrder(orderDomainObject); // this is @Transactional
    }
    But then in the services tier:

    Code:
    // this service class is @Transactional
    
    // ...
    
    pubic void submitOrder(Order orderDomainObject) {
         orderDomainObject.save(); // ends up calling repository
    }
    So then, why not inject the repository into the service and NOT have a save() method in the domain object?

    Perhaps a middle ground would be to have domain objects annotated as @Transactional(propagation = MANDATORY). Then you can indirectly use resources at the domain object level (via the repository), while at the same time preventing direct access from any entry path (calling into the repository via the domain object requires that the client has already started a transaction, which is documented in the code via the annotation)...

    Then again, perhaps I'm still too "transaction-script-oriented" and am missing the point?

    Any comments on the above would be appreciated.

  10. #10
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    akarypid, you bring up valid points. Not to revive this [at times somewhat inflammatory] thread, here's a "brief" answer:

    Transaction management is an application concern, not a domain concern... quite obviously. That is why it belongs in the "application layer" (the exact term used by Eric Evans, btw.) I personally have expressed my point of view many times in these forums: I don't think that persistence should be part of the Domain. In my opinion, it is very much an application-layer, use-case concern/implementation detail. DDD does not mean that all logic and persistence operations should be part of domain entities. It is the "DDD zealots" who, I think, take the idea way too far and fail to separate the essential object behavior from the application-specific logic that should - not contrary to what Eric Evans states in his book - live in the application layer. Services and repositories are part of the application layer. A domain "service" (or whatever you prefer to call an object that implements the logic that does not fit one particular domain entity but rather governs multiple domain entities in a context of particular application use cases) is the perfect point for transaction demarcation and domain-level security. (BTW, I would recommend defining transactions on the method level rather than on the class level.) Being part of the application infrastructure, repositories (or DAOs) should normally be abstracted within such "domain services." Spring provides very convenient stereotype annotations for such classes (@Service, @Repository, etc.) Note that none of this goes against the DDD principles. The most important thing, in my opinion, is to always properly separate the domain entity-level behavior from the application use-case level behavior. Failure to do so will result in a rigid domain model not reusable among applications within even the same enterprise. Implementing application use cases involves - usually - more than one domain class, may include transactional and secure operations, and should be implemented outside any given domain entity. That's what domain services (or whatever you want to call them if you hate the word "service") are for.

    HTH,
    Constantine
    Last edited by constv; May 18th, 2010 at 08:19 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •