Given:
- You have an architecture with the layers presentation, business and data.
- You are applying domain-driven design.
- You are using an object-relational mapper that lets you create object-oriented queries (e.g., NHibernate which lets you create HQL queries).
Question:
Into which layer should you put the object-oriented queries?
My thoughts:
I think putting them into the presentation layer will usually make no sense. But I don't know whether to put them into business or data layer.
Example (NHibernate):
Say your business logic needs some method GetCustomersPossiblyInterestedIn(Product p). You might then create a complex HQL query that selects customer objects where the customer has already bought a product that is in the same category as p.
Argument a)
On the one hand, I'd say this query is clearly business logic, because the decision that a customer is treated as possibly interested in a product based on whether she has bought a product in the same category is a business decision. You might as well select customers who have bought multiple products in the same category with a similar price, e.g.
Argument b)
On the other hand, the business layer should not depend upon the data layer, so directly using NHibernate in the business layer rings an alarm bell.
Possible solution 1)
Create your own object-oriented query language that you use in the business layer and translate to HQL in the data layer. I think this would cause a lot of overhead. If you use a query language based on query objects instead of a parsed query language, you could probably safe some effort, but my objection still applies.
Possible solution 2)
Directly using NHibernate in the business layer is O.K., because the abstraction level that NHibernate is able to provide with HQL, ISess开发者_StackOverflow社区ion etc. fits the business layer. There is no need to wrap it.
What do you think?
Edits:
See "Repository is the new Singleton" and "The false myth of encapsulating data access in the DAL" by Ayende Rahien for closely related discussions.
Definitely, avoid putting object-oriented queries into presentation layer. It should ONLY display/use data received from business logic layer (BLL). Without any querying. If you need to query results received from you BLL, then your BLL needs to be extended to provided such data that don't need to be queried.
Your idea to use 'object oriented query language' seams like a good. Usually, this "language' is your DAL :) I would say good example of "object oriented query language" is a properly implemented Data Access Layer (DAL).
From my perspective you DAL should implement 80-90% of all functionality and provide set of functions like that:
- Customer GetCustomerById(int customerId);
- List GetLastRegisteredCustomers(int count);
- etc...
These functions provide the biggest part of required functionality that don't need to be queried.
For all other 10-20% of rarely used queries (you named them "object oriented") your DAL should implement method/methods that return IQueryable result, I would say at least 'GetAll()' method and probably few customizations:
- IQueryable GetAll();
- IQueryable GetCustomerByCountry(int countryId);
in this case if you will need to find a customer in a country registered this year on you BLL you will call:
List<Customer> customers = GetCustomerRepository()
.GetCustomerByCountry(countryId)
.Where(customer=>customer.RegisterDate.Year==year)
.ToList<Customer>()
;
Guess, you know what provide IQueryable<> interface.
How to work with Linq under NHibernate: Linq to NHibernate.
One additional hint: I would recommend to use 'Repository' pattern for you DAL implementation. Some time ago i used this for general idea: http://habrahabr.ru/blogs/net/52173/ (if you can't read Russian translate whole page with google - it should be readable).
Hope that helps.
I prefer to wrap NHibernate (or other ORM) using some sort of a Repository. For eg. in the case of NHibernate the Repository would wrap the NHibernate session. This repository could be per class (So CustomerRepository and OrderRepository) or if you don't have too many classes in your domain, you could start with a single Repository as well.
This is then the perfect place to put Criteria queries (LoadAllByName, LoadCustomerWithOrder, etc.). If you later need to switch to a different ORM or even a different persistence mechanism (very rare, I would think), you can swap out the whole Data layer including the Repository.
Queries belong in the repository. You may have a function signature for GetCustomersPossiblyInterestedIn(Product p) anywhere it is necessary, but the query itself should only be in a repository class
精彩评论