Схема проектирования уровня обслуживания С#

Мы изучаем создание нового проекта и хотим исследовать его с использованием шаблонов Repository и Service, целью которого является создание слабосвязанного кода, который полностью тестируется с использованием макетов репозиториев.

См. ниже основную идею архитектуры. Мы будем использовать интерфейсы для описания репозиториев и внедрить их в уровни обслуживания, чтобы удалить любые зависимости. Затем, используя autofac, мы подключим службы во время выполнения.

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
    }
}

Есть несколько вопросов, с которыми у нас возникают проблемы с поиском наилучшего пути вперед.

1) Если служба воспроизводит методы CRUD, как представляется, мы можем воспроизводить код без реальной выгоды. Или пользовательский интерфейс непосредственно вызывает репозитории?

2) Что происходит, когда службе необходимо вызвать другую услугу. В нашем примере выше, если служба электронной почты должна получить все заказы, добавим ли мы службу заказа в службу электронной почты?

Надеюсь, это имеет смысл

Ответ 1

Служба электронной почты не должна знать о таких услугах, как OrderService, вам нужно Mediator для работы с Email && заказами, чтобы они будет отделяться, или Adapter адаптировать IOrder к 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

Определить объект, который инкапсулирует, как взаимодействует набор объектов. Посредник продвигает свободную связь, удерживая объекты от ссылки на друг друга явно, и это позволяет вам изменять их взаимодействие независимо друг от друга

Адаптер

Шаблон адаптера (часто называемый шаблоном обертки или просто оболочка) - это шаблон проектирования, который переводит один интерфейс для класса в совместимый интерфейс

Ответ 2

Взгляните на Domain Driven Design. DDD перемещает большую часть логики в сущности (Order, Email) и позволяет им использовать репозитории.

1) Если служба воспроизводит методы CRUD, как представляется, мы можем воспроизводить код без реальной выгоды. Или пользовательский интерфейс непосредственно вызывает репозитории?

Служба в DDD используется, когда вы обнаруживаете, что вы пишете бизнес-логику вне объектов.

2) Что происходит, когда службе необходимо вызвать другую услугу. В нашем примере выше, если служба электронной почты должна получить все заказы, добавим ли мы службу заказа в службу электронной почты?

Внесите его в конструктор. Однако служба заказа должна отправить электронное письмо, а не наоборот.

Подходом DDD будет создание OrderNotificationService, которое принимает событие домена OrderCreated и составит электронное письмо, которое оно отправляет через EmailService

Обновление

Ты пропустил меня. Дублированная логика никогда не бывает хорошей. Я бы не поставил метод в моей службе с именем GetAll, если у моего репозитория есть один. Также я бы не добавил этот метод в свою сущность.

Пример кода:

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

Order.Send() должен создать событие домена, которое может уловить OrderNotificationService.

Update2

Repository.Create - это всего лишь метод factory (google factory method pattern), чтобы получить все создания модели домена в одном месте. Он ничего не делает в db (хотя это может быть в будущих версиях).

Как и для order.Save, он будет использовать репозиторий для сохранения всех строк заказа, самого порядка или чего-то еще, что потребуется.

Ответ 3

Что бы я сделал,

  • Не вызывать репозитории непосредственно из пользовательского интерфейса, а вместо этого вызывать службу, а служба должна использовать репозиторий,

  • Я бы назвал метод репозитория заказов из службы электронной почты, таким образом, я бы просто ввел OrderRepository в службу электронной почты (а не службу заказа)

Ответ 4

вы можете использовать шаблон адаптера или использовать инструменты DI

public class EmailService
{
    private IOrderRepository _orderservice = null;

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

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