2

I am new to Domain driven design, so please excuse me if this question is trivial. I was reading about specification pattern and I believe understood its intent. Most of examples on web, shows its usage at two places:

  1. Inside repository method

  2. Inside Domain services/Application services.

But that would only work if EF entities and Domain objects are same. And I guess this not considered a good practices (using EF entities for domain object). Now my question is -

Should we write different specification rules for domain object and EF entities, Or there is a way to reuse the same rule for both? I guess if we don't c# expression and use reflection we can somehow achieve.

Pragmatic
  • 3,093
  • 4
  • 33
  • 62

2 Answers2

3

This is a topic that is near and dear to my heart. (Direct answer is below) Specification pattern is agnostic. All it does is determine if the condition you define (specify) is met. It is useful in both places. I see two primary benefits of Specification: It names your business rules, and it hides (encpasulates) your implementation. Follow-on benefits are that you can lock down your repositories if you desire or supply an easily understood list of queries or rules.

Specification as applied to domain objects is slightly different than used as queries, but rather close. A "QuerySpecification" should have query language in it that the target repository implementation (EF) can take and execute in the database and return 0 or more matches.

Applied to domain objects, it's more often used to check if a specific object "meets the specification" or to filter applicable objects in a list (either to exclude or include). The usage looks different, or it's a different aspect of the same concept.

I respectfully disagree with Zoran's suggestion. Returning IQueryable means you're giving application code (possibly someone else's application) the ability to directly query against your data store. Lazy vs Eager loading becomes a real tangle, too. You lose encapsulation and also implementation isolation, I think.

Remember also that EF represents an implementation of Repository and UnitOfWork. DbContext is the Unit of Work, while DbSet is a repository. Your goal in applying your own DDD interfaces is to make sure your domain does not depend on the EF Implementation, so your integration code there should be quite small.

So, direct answer.

I would suggest adding a method to your Repository interface that accepts IQuerySpecification and returns T. Your actual specifications are part of your domain. These are the rules for the combinations of data your organization needs. You could put them in a "shared kernel" (library), or have them reside in your application depending on your needs.

Note that ideally, by using the query specification, you can eliminate all the special purpose repository functions that often pop up because Specification separates the the thing being queried from the query. Eric Evans wrote it better:

The central idea of Specification is to separate the statement of how to match a candidate, from the candidate object that it is matched against. As well as its usefulness in selection, it is also valuable for validation and for building to order (https://www.martinfowler.com/apsupp/spec.pdf)

See also https://matt.berther.io/2005/03/25/the-specification-pattern-a-primer/

VeteranCoder
  • 444
  • 1
  • 4
  • 12
1

Keep in mind that, when working with EF repositories, you have the default option already available - let repositories return IQueryable<T> rather than IEnumerable<T>. Many programmers fear from that, but if done that way, then LINQ becomes your specification in the Infrastructure layer.

Then, in the Domain layer, you can apply Specification and Rule patterns to encapsulate conditions and rules that domain objects should be tested against.

There are other techniques out there as well, but this combination is what I normally apply in my projects.

Zoran Horvat
  • 10,924
  • 3
  • 31
  • 43
  • 1
    There are a lot of online resources discussing pros and cons of exposing IQueryable from repositories. Regarding Specification pattern, I have a module on that in one of my Pluralsight courses here: https://www.pluralsight.com/courses/tactical-design-patternsdot-net – Zoran Horvat Oct 22 '16 at 20:51
  • Please note that when returning an IQueryable you're violating the Don't Repeat Yourself and the Single Responsibility Principle. – Verbe Mar 01 '18 at 17:51
  • What you just said is that `DbContext` is designed to break DRY and SRP. Can you elaborate? – Zoran Horvat Mar 01 '18 at 21:45
  • @ZoranHorvat, Both are broken because any client of that object can execute queries to the database. Thus, SRP is broken, and DRY is broken if those clients do the same queries already specified to occur in the repository class. Worse, by making database access available "everywhere" all sorts of difficult to identify problems could arise, including horrific performance, unexplained db locks, table scans, etc. – VeteranCoder May 31 '22 at 21:38
  • @VeteranCoder Why would anyone place the same query in many places? In my reply, I have been clear that queries belong to the infrastructure layer, not *everywhere* as you stated. A layer of (infrastructure-related) classes around the repository is where you enforce DRY. Regarding SRP - how comes that adding more responsibilities to the repository will make it retain a *single* responsibility it already has? – Zoran Horvat Jun 01 '22 at 15:05
  • @ZoranHorvat, I did not imply that anyone should want to do so. I said that passing an IQueryable effectively enables that to happen, so anything that is visible outside the "infrastructure" library/facade should not see IQueryable, but instead some concrete local collection of results. People often filter, Count(), Any(), etc. collections. If one does that on an IQueryable, that pulls data from the database rather than an in-memory collection, or worse, throws a connection exception. – VeteranCoder Jun 01 '22 at 15:33
  • @VeteranCoder There is the whole misunderstanding here, the root of which I do not know. In my post I have clearly said that `DbContext` and its `IQueryable` belong to the infrastructural layer, where LINQ is the specification language. I do not see which other parts of the application, most notably the domain layer, could tamper with the database when they don't even have a reference to `DbContext` in the first place. – Zoran Horvat Jun 01 '22 at 19:21
  • @ZoranHorvat I see! So IQueryable is not passed out of the infrastructure layer at all? Then why did you explicitly say to have the repository interfaces return IQueryable? Repositories by definition are used directly by the domain and application code. – VeteranCoder Jun 02 '22 at 15:21
  • @VeteranCoder I see where I made the mistake - I was using the term *repository* as synonymous to `DbContext` in the context of working with EF, but that was not accurate for this post. – Zoran Horvat Jun 02 '22 at 21:43
  • 1
    @ZoranHorvat, I figured that was the case, but also wanted to ensure there was clarity on this point, as "there be dragons...". – VeteranCoder Oct 19 '22 at 14:53