Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Service Layer Design Pattern

We are looking into creating a new project and are wanting to explore using the Repository and Service layer patterns, the aim to is create loosely coupled code which is fully testable using mock repositories.

Please see below the basic architecture idea. We will be using interfaces to describe the repositories and inject these into the service layers to remove any dependencies. Then using autofac we will wire up the services at runtime.

public interface IOrderRepository
{
    IQueryable<Order> GetAll();
}

public class OrderRepository : IOrderRepository
{
    public IQueryable<Order> GetAll()
    {
        return new List<Order>().AsQueryable();
    }
}

public class OrderService
{
    private readonly IOrderRepository _orderRepository;

    public OrderService(IOrderRepository orderRepository)
    {
        _orderRepository = orderRepository;
    }

    public IQueryable<Order> GetAll()
    {
        return _orderRepository.GetAll();
    }
}

public class EmailService
{
    public void SendEmails()
    {
        // How do I call the GetAll method from the order serivce
        // I need to inject into the orderService the repository to use
    }
}

There are a few questions which we are having trouble finding out the best way forward.

1) Should the service be reproducing the CRUD methods, as it appears we may be reproducing code with no real benefit. Or should the UI call the repositories directly?

2) What happens when a service needs to call another service. In our example above if Email Service needs to get all the orders, do we inject the order service into the Email service?

Hope this makes sense

like image 227
user1223218 Avatar asked Feb 21 '12 11:02

user1223218


2 Answers

Email service should not be aware of services like OrderService, you need Mediator to work with both Email&&Order services so they would be decoupled, or Adapter to adapt IOrder to IEmail:

IEnumerable<IOrder> orders = orderService.GetAll();

// TODO: Create emails from orders by using OrderToEmailAdaptor
IEnumerable<IEmail> emails = ... 
emailService.SendEmails(emails);

public sealed class EmailService
{
    public void SendEmails(IEnumerable<IEmail> emails)
    {
    }
}

Mediator:

Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently

Adapter:

The adapter pattern (often referred to as the wrapper pattern or simply a wrapper) is a design pattern that translates one interface for a class into a compatible interface

like image 196
sll Avatar answered Nov 11 '22 19:11

sll


Take a look at Domain Driven Design. DDD moves most of the logic into the entities (Order, Email) and lets them use the repositories.

1) Should the service be reproducing the CRUD methods, as it appears we may be reproducing code with no real benefit. Or should the UI call the repositories directly?

A service in DDD is used when you find yourself writing business logic outside the entities.

2) What happens when a service needs to call another service. In our example above if Email Service needs to get all the orders, do we inject the order service into the Email service?

Inject it in the constructor. However, the order service should send an email, not the other way around.

The DDD approach would be to create a OrderNotificationService which takes the domain event OrderCreated and composes an email which it sends through the EmailService

Update

You missunderstood me. Duplicated logic is never good. I would not put a method in my service named GetAll when my repository has one. Neither would I put that method in my entity either.

Example code:

var order = repository.Create(userId);
order.Add(articleId, 2);  
order.Save(); // uses the repository

Order.Send() should create a domain event that the OrderNotificationService can catch.

Update2

Repository.Create is just a factory method (google factory method pattern) to get all domain model creations in one place. It do not do anything in the db (although it could in future versions).

As for order.Save it would use the repository to save all order lines, the order itself or anything else that would be needed.

like image 3
jgauffin Avatar answered Nov 11 '22 18:11

jgauffin