11

I'm wondering the following: suppose we are building a system where there needs to be some filtering functionality to search for some entity. For example, one might want to apply the filtering to a table listing the entities to find something, or use it to generate a report on a filtered set, whatever.

The point is: we need to have a filtering logic somewhere. One bad way of doing this would be to replicate the filtering logic where needed. I've done that once and it is one terrible idea.

On the other hand, I belive there should be a method like Filter(FilteringOptions filteringOptions) designed to perform the filtering operation and return the filtered list of entities.

Now, IMHO, the filtering logic is a kind business logic. The business experts are the ones who knows how the filtering takes place, what things are filtered and how. Because of that, I believe the filtering logic should be located in the domain layer.

I've found two options for doing this: embeding the filtering method in the corresponding repository for that particular entity, or else, creating a domain service like EntityNameSearchService which would consume the repository to perform the filtering.

I'm still confused which one would be the better way. So, if I'm trying to use DDD properly, where this filtering logic should be? On the repository or in a separate service?

3
  • 2
    Where does the filtering logic make the most sense to you, from a maintainability and usability perspective? Jul 20, 2016 at 15:58
  • At a first glance it seemed that in the repository, since it is the object responsible for, among other things, retrieving the entities. On the other hand, the filtering logic needs not to be applied just on retrieval, it can be applied to any list of entities. Indeed, thinking a little more about it, the domain layer contain just the interfaces of repositories. The actual implementation lies on a Data project and has to be coupled with the actual persistence mechanism, while services are implemented on te domain. From this point of view creating a filtering service seems to make more sense. Jul 20, 2016 at 16:35
  • 1
    Filtering in service layer will cost more time than getting already filtered data from the repository through a targeted method, but you may reuse the generic Get* method and introduce different or user-defined filters in the service layer. The decision is mostly up to you.
    – Andy
    Jul 20, 2016 at 17:31

1 Answer 1

8

I'm wondering the following: suppose we are building a system where there needs to be some filtering functionality to search for some entity. For example, one might want to apply the filtering to a table listing the entities to find something, or use it to generate a report on a filtered set, whatever.

You should observe that this point that your use cases for filtering center around reads, rather than writes. This is the normal pattern -- a write is typically addressed to a specific aggregate root in your domain.

If you are performing a read, then you don't actually care about aggregates - you don't care about the enforcement of the business invariant because you aren't actually trying to change anything. You just care about state.

Which may mean that it makes sense to bypass the domain model completely.

Just something to think about.

Now, IMHO, the filtering logic is a kind business logic. The business experts are the ones who knows how the filtering takes place, what things are filtered and how. Because of that, I believe the filtering logic should be located in the domain layer.

Yes, and no. You are drawing upon the ubiquitous language to describe the filter. So you are definitely using the domain vocabulary.

But you don't have any "behavior", in so far as you aren't actually modifying the book of record, so you don't have the invariant to worry about.

On the other hand, I belive there should be a method like Filter(FilteringOptions filteringOptions) designed to perform the filtering operation and return the filtered list of entities.

You are very close to the idea of "Specification". It's basically a predicate than a repository can use to identify which artifacts match some arbitrary criteria.

There are some traps to be aware of. Greg Young touched on them some time back, but I'll summarize here.

First, the abstraction of running a predicate against a collection is O(N). You'll probably want something nicer, especially if your persistence store is smart about indexing. Your persistence component will likely want to be able to transform it to a implementation specific constraint (example: taking a specification and turning it into a where clause on a prepared statement).

Second, an interface is a means of documenting the contract served by the persistence component. "Make the implicit explicit" -- if you describe what it is that you really need, then the interface tells you something about what characteristics are important for your data store, which gives you a single place to look when trying to evaluate whether an alternative store is suitable.

(Of course, the implementation of that interface might just be an adapter that creates the specification from the method arguments, and forwards that along. That's OK, you've captured the actual requirement).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.