C# Service Layer Design Pattern

你说的曾经没有我的故事 提交于 2019-12-31 08:52:31

问题


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


回答1:


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




回答2:


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.




回答3:


What I would have done is

  1. Not call repositories directly from the UI, but call the service instead and service should use the repository,

  2. I would the call the method of Order repository from Email service, this way I would have only inject OrderRepository to Email Service (not Order Service)




回答4:


you can use adapter pattern or using DI tools

public class EmailService
{
    private IOrderRepository _orderservice = null;

    public EmailService(IOrderService orderservice)
    {
        _orderservice = orderservice;
    }

    public void SendEmails()
    {
        _orderService.GetAll();
    }
}


来源:https://stackoverflow.com/questions/9377172/c-sharp-service-layer-design-pattern

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