Specifying Specifications
Specification: An explicit statement of the required characteristics for a matching entity / object.
Specifications are very useful concepts when you need to think about searches. They are useful because they seperate what you are searching from how you are searching.
In the example to the right, we have a specification for a job opening. We can use it to search for matching job openings. But couldn't we do it directly? Yes we could, but then we would dealing with both what and how at the same time.
Let us say that we want to search for an opening matching a male candidate, age 28, shall we? How do we handle that?
Pretty simple, actually:
OpeningSpecification spec = new OpeningSpecification();
spec.Age = 28;
spec.Gender = Gender.Male;
spec.FindAll(openingRepository);
The specification is responsible for translating that to a query that would return the correct result, which this translation can be wildly different than the specification itself. For example, let us take the age. We want opening that match a person whose age is 28, but the opening doesn't have a DesiredAge property, they have min / max ages that they allow.
That fact means that we search for a male doesn't mean that we need to exclude every opening where the max/min age reject someone with age of 28 years. But we don't care about that when we use the speficiation, we just want to find the matching opening.
From an implementation perspective, we use the property setters as a way to incremently build the query, here is how I built the Age property:
[Property] public virtual int? Age { get { return age; } set { age = value; if (value == null) return; criteria .Add(Expression.Le("MaxAge", age) || Expression.IsNull("MaxAge")) .Add(Expression.Ge("MinAge", age) || Expression.IsNull("MinAge")); } }
As an aside, consider the usage of the operator overloading there, I completely forgot to use that before, and that is a shame, since I was the one that added that to NHibernate. Andrew Davey reminded me how useful this is.
You may notice that I have a [Property] on the specification Age property, and that it carries some unusual properties such as Id, Name and Username. Those are used to persist the specification, so I can save it and then load it again, extremely useful if I want to save a search for "Males over 30", and re-run it later.
Another advantage here is that I have the possibility to construct a specification easily from another object. You can see the FromCandidate() method on the specification above, it build a specification from the candidate that matches job opening that the candidate is eligable for.
All in all, a very useful concept, and it gets even nicer if you throw MonoRail into the mix, but this is a matter for another post.
Comments
Nice, a very readable pattern. Although I think that using the setter that way might introduce a side effect that isn't very obvious when using the class (if you e.g. set the property twice)
This is really nice. Something I think this will need is an ability to tell the specification how to order its results. Here's an idea of a way to achieve this. Derive OpeningSpecification from BaseSpecification<T>, which would have something like this:
protected DetachedCriteria criteria;
public string OrderBy
public bool OrderAscending
public ICollection<T> FindAll
private void AddOrder(string propertyName)
In FindAll you would call AddOrder, which would add an Order to the criteria object, before calling the Find.
I have some code for AddOrder (from StoryVerse) that interprets a nested property string and adds an Order to a detached criteria object.
Or if you don't like inheritance, you could do the same thing using composition instead.
Tim,
I took the general idea from Story Verse!
We usually call this search classes, or criteria classes, not specifications. Specification are tight to the domain, they carry insight and domain logic, and they only exists to make some logic very clear and visible.
Overusing the word specification for something that it clearly is not might bring more confusion about what DDD is.
But those are part of the domain, they are used in business logic process and present some very interesting possibilities.
Note that I talked about persistence and the ability to construct from many sources.
In DDD terms, given a container and a destination, I create a specification that will match the routes that will get the container to its destination.
I am not talking about finders (my own term for classes that does similar things, but are strictly helpers).
Quoting DDD's book:
"A specification is a predicate that determines if a object does or does not satisfy some criteria"
OpeningSpecification is not a predicate.
So you would feed a criteria to a specification?
Maybe, but that's not how I use them.
CanStudentEnrollInCourseSpecification
{
public bool satisfy(Student student)
{
}
}
I usually pass a single object instance. The thing is, it feels natural to put this kind of logic on the student class. The specification is a way to make it clear on your domain some piece of logic, instead of burying it on some method somewhere...
Okay, I see you point, but I think that you are thinking about a specification only in terms of validation.
This paper:
http://martinfowler.com/apsupp/spec.pdf
is talking about the spec pattern, it was written by Fowler & Evans, and they talk about selection, validation and construction to order:
Selection: You need to select a subset of objects based on some criteria, and to refresh the selection at
various times
Validation: You need to check that only suitable objects are used for a certain purpose
Construction-to-order: You need to describe what an object might do, without explaining the details of
how the object does it, but in such a way that a candidate might be built to fulfill the requirement
They seem to be talking specifically about validating a specific object, but I think that this is a direct extension of this.
All of thse are covered by the book, but they are still implemented as predicate, action on a single entity.
foreach(Invoice inv in invoices)
{
isOverdueSpec.SatisfiedBy(inv); // selection
}
Probably we need to take this to the DDD mailing list.
Because to me this looks like a parameterized spec that simply filter the data from the DB directly, instead of loading it and then filtering it.
As I see it Search Screen functionality is not part of the domain layer. Search screens will vary depending on the application using the domain hence this kind of specification should be in the Application layer.
Well thats just a humble mans opinion :-)
That is because a search screen is only one such scenario that can use them.
Consider the ability to say: Watch for any incoming opening that match this person. This is most certainly a domain concept.
I see that !!
But what is the difference then between this kind of specification pattern and Evans definition of Repositories??
I see it more as a Repository then??
Repository is how you interact persistent storage
In this case, I am using specification in conjunction with the repository to get the values.
The specification build a query object, and pass it to the repository, the repository takes this query object and use it to pull the data out.
Hi,
I use for a long time the Term "XyQueryDescription", which often gets for some domain objects very feature rich. Sometimes it provides as well everything needed for paging.
UserQueryDescripion
+int MinAge
+int MaxAge
+int CurrentPage
+int PageSize
+bool NextPage()
+bool HasNexPage()
+static UserQueryDescription GetPremium()
+staitic UserQueryDescription GetInactive()
...
+Filter
+Filter.AddId
+Filter.AddCondition
.. etc
The XyDataService then has method like:
GetBy(XyQueryDescription description) which returns a list of List<OfSomething>
The BusinesServices wraps the DataServices and often hide details on creating the XyQueryDescription. If the thinks get more complicated I could think of Specifications to create QueryDescriptions. But I could imagine that this is rarely needed.
I find myself using that kind of object not only in Database Query Context, but as well when working on list of objects.
So from my point of view it is good idea to decouple the NHibernate CriteriaCreation and put into the DataServices...
(Currently I did not found a good reason to separate the paging, even if not needed it does not interfere)
Do I miss the point? What do you think?
On the word choice, another case: Microsoft MapPoint webservice provides a set of "FindSomethingSpecification" classes. For example, FindNearbySpecification: http://msdn2.microsoft.com/en-us/library/aa502402.aspx
It would seem that your "Males over 30" example could be problematic here. You can no longer specify Age, but need AgeMin and AgeMax.
You may also get into trouble with using the setters. If someone was to set the age twice, only one would be effective....
I borrowed some ideas from the folks at Story Verse too, but they do use the suffix "Criteria" for their classes (not Specification). I have to admit that I ended up sticking with term Criteria mainly because I thought the word Specification was too long ;p
In your example, if the property Age gets set twice you would have a bug, right?
Over here, I opted to create an instance of DetachedCriteria everytime an implict cast happens - because it seems safer (others could alter the query object state).
--
ps: is this DDD mailist public? I'd like to follow the discussion..
Alexey,
Sorry, you are correct.
I am thinking about the other side of the OpeningSpecification, the CandidateSpecification, where you can set max age.
In OpeningSpecification you sepcify the candidate age, and it matches opening that will fit that age.
Pedro,
The DDD is on yahoo, you can search it there.
Yes, if you set the property twice Bad Things happen, but it makes the programming model very easy
I came up with something similar, but separated the concept of specification from query, but allowed one to implement the other.
So we start with IQuery<T> which you can pass to the Dao to get a list of T's. also ISpecification<T> is a specification ala Evans which supports IsSatisfiedBy.
Say I have a InvoiceableOrderSpecification and I want to find all the invoices that I can issue, but there's a large number of them, most of which will be already invoiced or not meet the criteria in some way.
If I define IQueryableSpecification<T> as follows...
then I can use the specification to obtain a first-cut query that reduces my search space to likely candidates rather than processing the entire respository.
I was just going over this last night. Evans doesn't narrow the use of Specification to mere predicate (IsSatisfiedBy) but also includes Search and Create applications on pp.227-230.
But I suppose it degenerates into a semantic discussion
Comment preview