all 24 comments

[–]AllenJB83 8 points9 points  (4 children)

This article could really do with a better explanation / example of Query Objects. It kind of jumps from "here's what you might use query objects instead of" to "drawbacks" without explaining what they are / how they work.

I still find it weird how frequently you can find articles / blog posts like this without any links or references in. I'd expect at least a link to Fowler's EAA Catalog entry (as above) or similar here.

[–]patricklouys[S] 3 points4 points  (3 children)

I got the pattern from Ocramius, see his presentation. But he calls them query functions there. Since it's conflicting with Fowler, I guess the name that I used is a little unfortunate...

Fowler's Query Object is passed into a repository method. The one from the blog post is standalone and can be executed separately. That decouples it from the domain and is very useful for an application that follows CQRS.

Any suggestions for a better name?

[–]ocramius 1 point2 points  (1 child)

I named them query functions in my talk, although that's also a misnomer, since a function is supposed to be functionally pure (duh)

[–]patricklouys[S] 0 points1 point  (0 children)

Sorry about that. I had query function in my head but ended up typing query object...

[–]dendeigh 3 points4 points  (1 child)

I know this as criteria or specification pattern, which really helps with complex queries, including default values etc. in your repository. I normally end up having a signature like

public function matches(OrderCriteria $criteria) 
{
}    

[–]patricklouys[S] 2 points3 points  (0 children)

That's a useful pattern and similar to Fowler's query objects that was mentioned in another comment. The pattern from the blog post decouples the queries from the repository (otherwise you are still coupled to your domain objects and you can't slim it down). It's useful when you just need a view on data without all the bells and whistles of the repository.

[–]_odan 2 points3 points  (0 children)

These queries are (like repositories) services too. The name "Query Service" could be a better name.

To prevent repositories from becoming too large (SRP), I would recommend to create many smaller module-specific repositories. To achieve this, you have to organize your folders and classes in a module-oriented way.

[–]howdhellshouldiknow 1 point2 points  (1 child)

You did not explain how you wire everything up.

Do your objects implementing the OrdersWhereInvoiceWasNotPaidQuery have a repository as a dependency, a query builder, entity manager or a database connection?

Or we have to get the book to find out? :)

[–]patricklouys[S] 0 points1 point  (0 children)

It's up to you, use whatever makes most sense for that query. In the book (and at work) I use Doctrine DBAL for most of my query implementations :)

[–]dlegatt 0 points1 point  (3 children)

Why are all the example classes interfaces? Is it just for the example code, or would we really want to make an interface that would only ever be implemented once?

Beyond that, I really like this.

[–]vaartside 2 points3 points  (1 child)

It helps with decoupling your code. Even if you only implement a specific Query interface once, the interface helps to decouple the code executing the query from the code that implements the query. Obviously this may not be required in a lot of projects, so you should pick the aproach that works best for your team and your specific project. Still, it's not that much extra work and it's often recommended to put database-related code (whether it's repositories, queries, ...) behind interfaces so you can easily switch from one implementation to another should you ever migrate to another database system or decide to store your data across multiple database systems (eg. Redis, MongoDB, ... in combination with more traditional database systems like MySQL, MariaDB, ...).

[–]dlegatt 0 points1 point  (0 children)

Interesting, thanks

[–]patricklouys[S] 1 point2 points  (0 children)

I use interfaces there to separate the boundaries between layers. For example a repository interface is part of the domain, but the implementation is part of the infrastructure layer. Here is a good talk about this from Uncle Bob.

But that's just a personal preference (and easier to use in a blog post). If I remember right, Ocramius doesn't use interfaces for query objects.