I’ve been playing around with asp.net MVC3 a bit and have been struggling to decide where to place my business logic. I’ve settled on using a service layer for now:
public class AnimalsService : IAnimalsService
{
private readonly IAnimalsRepository _animalsRepository;
public AnimalsService(IAnimalsRepository animalsRepository)
{
_animalsRepository = animalsRepository;
}
public IQueryable<Animal> GetFiveLeggedAnimals()
{
...
}
}
The controller would look something like this:
public class AnimalsController : Controller
{
private readonly IAnimalsService _animalsService;
public AnimalsController(IAnimalsService animalsService)
{
_animalsService = animalsService;
}
public ViewResult ListFiveLeggedAnimals()
{
var animals = _animalsService.GetFiveLeggedAnimals();
return View(animals);
}
}
I have basic CRUD logic in the repository (All, Find, UpdateOrInsert, Delete). If I want to use these CRUD methods in my controller:
1) Do I have to create wrapper methods in the service for these respository calls?
2) Would it not make more sense for me to just include the GetFiveLeggedAnimals method and other business logic in the repository?
3) Could I implement the IAnimalsRepository interface in the AnimalsService and then call the base methods (I realise this is possible but I assume its bad practice)?
Mostly, yes. Typically, you want to offer CRUD for your domain models in the service layer. This way, the controller does not need to work with the repository directly (in fact, it never should). You can add more more sophisticated logic later without having to change external code. For example, consider you wanted to implement a newsfeed. Now every time a five-legged animal is inserted, you want to create a news item and push it to five-legged-animal-fans. Another common example is email notifications.
Business logic should be in the Service Layer or in the Domain Model objects themselves, and only there. In fact (see 3), I wouldn’t specifically offer an
IAnimalRepositoryat all, if possible.For instance, in a NoSQL-Environment, the database driver pretty much is a repository. On the other hand, when using a complex ORM mapping and stored procedures (where part of the biz logic is in the DB), you don’t really have a choice but offer explicit interfaces that know the stored procedures.
I’d go for a
IRepository<T>and use the Query Object pattern, if possible. I think LINQ can also be considered a Query Object / Repository based pattern.To call the base methods, you’d have to inherit from a concrete implementation, e.g. from
ConcreteAnimalsRepository.Also, if your service implements the
IAnimalsRepositoryinterface directly or indirectly, it makes the (unfiltered) CRUD operations available to everyone.My take: Don’t inherit, aggregate. A service layer has a repository, but it isn’t a repository itself: The service layer handles all the additional application logic (permissions, notifications) and the repository is a very thin wrapper around the db layer.
As an extreme example, what if deleting something directly was forbidden, and only the service would be allowed to make use of it when inserting a newer revision of sth.? This can be easily built when aggregating.