Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specification pattern and DDD

Within the context of a personal play project for learning more about DDD patterns, I am missing a Specification object for my filters.

Looking around for examples, it seems that everything (like LinQ) is oriented towards SQL databases. However with many NoSQL databases most queries, even just a "select * from table" requires a predefined view. Nevertheless, if the repository is mapping a web service even the type of queries is much more rigid.

Are there variations of the Specification pattern taking into account the limitations of non SQL databases? I have the feeling that this would require the use of inheritance and static declarations to support different type of persistence backends.

How should I combine "sorting" and "filtering" in my repositories? As an example consider the repository for a list of Order items.

(Query)findAllSortedByDate;
(Query)findAllSortedByName;
(Query)findAllSortedByQuantity;

So these are different type of sorting when displayed by a table. Since I might handle large results, I never considered sorting or filtering in my views or view models. Initially I thought about a Proyection class that selects the right query from a repository according the user actions. However this does not work well if I want to combine different sort strategies along different filters.

Obviously I need some type of "specification" object, but I am not sure if:

  1. Should I use them for my repositories, making them smarter? Or should I use them for my view models?
  2. How can properly restrict my specification objects for a good polyglot persistence?

Originally I thought about performing any query with a Repository acting as a collection-like interface but now I am noticing that a view model may also behave as "stateful" collection-like interface while the former are "stateless" collection-like interfaces.

  1. Overall should I try to keep any type of sorting/filtering inside my repositories? It seems that doing so, might add unnecessary complexity if all the query results can be loaded into memory.

UPDATE: To spice up this question, consider also that although NoSQL views can be filtered and sorted, a full text search might need an external indexing engine such as Lucene or SQLite-FTS providing only the unique identity of the Entities for a query that must be sorted and filtered again.

like image 304
SystematicFrank Avatar asked Mar 23 '23 19:03

SystematicFrank


2 Answers

On FILTERING

By "collection-like interface", Fowler doesn't mean something that expose an API resembling array or list: it has nothing to do with ICollection<T>, for example! A Repository should encapsulate all the technological details of the persistence layer, but its API should be defined so that it is expressive in the business domain.

You should think of specifications as logical predicates that are relevant in the domain (indeed they are first class citizens in the domain model) that can be composed both to check different qualities of an entity and to select entities from a set (a set that can be either a repository or a simple list). For example, in a financial domain model that I designed for an italian bank, I had DurationOfMBondSpecification, StandardAndPoorsLongTermRatingSpecification and so on.

Actually, in DDD, specifications come from business requirements (often contractual bounds) that must be enforced by the software during its operations. They can be used as an abstraction for a filter, but this is more like a fortunate side effect.

On SORTING

Most of times sorting (and slicing, and grouping...) is just a presentation concern. When it's a business problem, proper comparers (and groupers so on) should be distilled as domain concepts from the domain expert's knowledge. However, even with it's just a presentation concern, it's far more efficient to handle it in the repository.

On .NET, one possibile (and very expensive) solution to these problems is writing a custom LINQ provider (or more than one) that can translate all the query that can be expressed with the ubiquitous language to the desired persistence layer. This solution however has a major downside, if you can't translate all the queries from the begining, you will never be able to estimate the effort for a change to the application using the domain: the time will come when you will have to deeply refactor the QueryProvider to handle a new complex query (and such refactoring will cost you much much more than you can afford).

To address this problem, in the (work-in-progress and very ambitious) Epic framework (disclaimer: I'm the core developer), we choosed to join the specification pattern and the query object pattern, providing a general purpose API that enables clients to filter with specifications, to sort with comparers and to slice with integers, without the (unpredictable) costs of LINQ.

In the Fowler scheme (below), we are passing both the specification (aCriteria) and the ancillary sorting requirements to the repository:

enter image description here

As an alternative, you could just use custom repositories: it's by far a cheaper approach, if you don't have thousands of different types of queries.

Bonus Solution

A quick, but still correct solution is to "just" use your persistence language as is for queries. DDD is for complex operative boundaries and querying (most of times) is not operative: thus you can simply use the language provided by your SQL or NoSQL database to retrieve the projections you need and/or the identifiers of the entities you need to manipulate. You will see how the set of data that you query is very different from the set of data that are required to ensure domain invariants!

This is why, for example, sometimes, serialization files can be the best approach to the domain persistence for the problem at hand.
After all, what are filesystems, if not the most diffused NoSQL databases! :-D

like image 165
Giacomo Tesio Avatar answered Mar 25 '23 20:03

Giacomo Tesio


In DDD, a Specification is a predicate that applies to a domain object. It covers the "filtering" need but not the "sorting" need, since sorting doesn't use a boolean function of a domain object but property selection and sort direction.

What I typically write when I need filtering and sorting is a repository method along these lines :

findAll(Specification<Order> specification, 
        SortingOptions<Order> sortingOptions)

At that point, we don't need to think about the underlying persistence mechanism at all. Your domain layer repository interfaces shouldn't be shaped by your data store but by domain needs.

If your data source makes it hard to create queries that filter and/or sort, you may perfectly choose to filter/sort objects on the query result in memory afterwards. However, rather than doing so in a view model, it's a better idea IMO to create a concrete implementation of your repository that will load objects in memory and perform the sort/filter operation right there.

All future additional presentation layers will benefit from it, and it will be much more convenient to adjust your repository implementation if it turns out the data source (whether it be a NoSQL database, a web service...) fixes its flaws over time.

like image 27
guillaume31 Avatar answered Mar 25 '23 19:03

guillaume31