In which layer should Specification Pattern objects be “new'ed up”?

倾然丶 夕夏残阳落幕 提交于 2019-12-03 08:19:57
autonomatt

Short answer:

You use Specifications mainly in your Service Layer, so there.

Long answer: First of all, there's two questions here:

Where should your specs live, and where should they be new'd up?

Just like your repository interfaces, your specs should live in the domain layer, as they are, after all, domain specific. There's a question on SO that discusses this on repository interfaces.

Where should they be new'd up though? Well, I use LinqSpecs on my repositories and mostly ever have three methods on my repository:

public interface ILinqSpecsRepository<T>
{
    IEnumerable<T> FindAll(Specification<T> specification);
    IEnumerable<T> FindAll<TRelated>(Specification<T> specification, Expression<Func<T, TRelated>> fetchExpression);
    T FindOne(Specification<T> specification);
}

The rest of my queries are constructed in my service layer. That keeps the repositories from getting bloated with methods like GetUserByEmail, GetUserById, GetUserByStatus etc. In my service, I new-up my specs and pass them to the FindAll or FindOne methods of my repository. For example:

public User GetUserByEmail(string email)
{
    var withEmail = new UserByEmail(email); // the specification
    return userRepository.FindOne(withEmail);
}

and here is the Specification:

public class UserByEmail : Specification<User>
{
    private readonly string email;

    public UserByEmail(string email)
    {
        this.email = email;
    }

    #region Overrides of Specification<User>

    public override Expression<Func<User, bool>> IsSatisfiedBy()
    {
        return x => x.Email == email;
    }

    #endregion
}

So to answer your question, specs are new'd up in the service layer (in my book).

I feel like the only thing that should be injected into Model classes are "services"

IMO you should not be injecting anything into domain entities.

Add to this, that the Specification in question requires a Repository in order to determine whether it's "satisfied" or not.

That's a code smell. I would review your code there. A Specification should definitely not require a repository.

A specification is an implementation check of a business rule. It has to exist in the domain layer full stop.

Its hard to give specifics on how you do this as every codebase is different, but any business logic in my opinion needs to be in the domain layer and nowhere else. This business logic needs to be completely testable and coupled loosely from UI, database, external services and other non-domain dependencies. So I would definitely rule out 1, 2, and 3 above.

4 is an option, at least the specification will live in your domain layer. However the newing up of specifications depends really again on the implementation. We usually use depencency injection, hence the newing up of pretty much all of our objects is performed via an IOC container and corresponding bootstrapping code (i.e. we usually wire the application fluently). However we would never directly link business logic directly to e.g. UI model classes and the like. We usually have contours/boundaries between things such as UI and domain. We usually define domain service contracts, which can then be used by outside layers such as the UI etc.

Lastly, my answer is assuming that the system you are working on is at least some way complex. If it is a very simple system, domain driven design as a concept is probably too over the top. However some concepts such as testability, readibility, SoC etc should be respected regardless of the codebase in my opinion.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!