Usage and Difference between Service.Pattern and Repository.Pattern

Apr 25, 2014 at 11:44 PM
Edited Apr 27, 2014 at 8:49 PM
I am not quite getting the difference between these two.

After comparing the interfaces for IService and IRepository/Async, they look almost identical. When i look at the sample CustomerService class, it seems to be specific to just the customer entity, since it is a generic class of Customer.

I would expect a service class to take in multiple repositories, as well as other domain and application services (e.g. IEmailSender, IOrderProcessor, IShippingService, etc.), all injected via the constructor, using an IoC container.

How is the Service.Pattern framework meant to be used? How does it differ from the Repository.Pattern interfaces? What is the intent, since I'd expect the service classes to leverage one or many repositories of different entities to query and update data, not just one entity.
Apr 29, 2014 at 6:16 AM
tafs7, the difference is the responsibility.

In this framework, you have a generic repository that is responsible for all the entity-specific CRUD functionality. Then you have an optional non-generic repository, eg, CustomerRepository.cs which is responsible for your non-generic queries related to the entity, i.e. GetCustomerOrderTotalByYear. Then you have the service layer, which you correctly presume would have a higher responsibility (all business logic not handled by a repository query), but its implementation is dependent on what you are trying to achieve.

Depending on your needs, you may implement a data-centric or a (DDD) Domain driven design approach. I suspect a CQRS approach may also be achievable but I haven't confirmed that yet.

A data-centric approach is appropriate for simple admin screens, or cases where the domain is really simple and not expected to change much. The Customer example in this solution demonstrates this approach and the customer table entity is exposed through all the layers as is. In this approach, the service layer doesn't do much but pass thru to the repository. If we had say some requirement, like suggesting a username derived from the customer's name, that logic would be a method in the service, which may operate on the entity as it passes through. The responsibility of the service class in this approach is to implement business logic on data entities.

In a DDD approach, the responsibility of the service class is to implement business logic that does not logically belong in any one domain object, typically because it applies to multiple objects. The IService class as it is written here would not work well for this. However, you could implement your domain object, which itself implements multiple IRepositories and a Domain Service, which resembles the 'service' you're used to seeing.
Marked as answer by lelong37 on 4/28/2014 at 11:13 PM
Apr 29, 2014 at 7:10 AM
Edited Apr 29, 2014 at 7:12 AM
Well said Bill_Braun, the only thing I would add is on the Service pattern comment. So the Service pattern implemented here is with the intent that a service e.g. CustomerService primary concern is all things "Customer" related. Obviously, there will always be cases where the responsibility of CustomerService will spread into other domain boundaries e.g. Updating a customer's account, order, invoice, etc. Although the Service pattern here has made the IRepository<Customer> easily accessible because we inherit IService<Customer> (again, because we know this service will be working within the Customer domain), you would simply inject other repositories into the service class, which is why the Service pattern here (Service.cs) is not generic.

We don't inject other IRepository<T>'s, out of the box, because we can't predict all the other domain boundaries you intend to work within in this Service.
public class CustomerService : Service<Customer>, ICustomerService
{
    private readonly IRepositoryAsync<Customer> _repository;

    public CustomerService(
        IRepositoryAsync<Customer> customerRepository,
        IRepositoryAsync<Order> orderRepository
        )
        : base(customerRepository)
    {
        _repository = customerRepository
    }

    public decimal CustomerOrderTotalByYear(string customerId, int year)
    {
        return _repository.GetCustomerOrderTotalByYear(customerId, year);
    }

    public IEnumerable<Customer> CustomersByCompany(string companyName)
    {
        return _repository.CustomersByCompany(companyName);
    }

    public IEnumerable<CustomerOrder> GetCustomerOrder(string country)
    {
        return _repository.GetCustomerOrder(country);
    }
}
Apr 29, 2014 at 7:28 AM
The IService and IRepsositry Async do look identical out of the box, which is the intent and by design. However, the difference is that all the CRUD methods Service.cs which implements IService and is which all your Service class inherits are overidable, meaning this is where you would overirde Delete and implement any business logic that may occur pre or post the delete, again, the Service.cs provides all the placeholders for you to inject any pre and post business logic before any of your CRUD.
Apr 30, 2014 at 6:46 PM
makes sense.

I think the sample for the service pattern should reflect these intentions are little more to clear up any confusion. Also, I think you should take what Bill said and put that in the wiki/docs. It was a very good explanation :)

--T