2012-02-21 4 views
10

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

См. Ниже основную идею архитектуры. Мы будем использовать интерфейсы для описания репозиториев и внедрить их в уровни обслуживания, чтобы удалить любые зависимости. Затем, используя 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) Что происходит, когда услуге необходимо вызвать другую услугу. В нашем примере выше, если служба электронной почты должна получить все заказы, добавим ли мы службу заказа в службу электронной почты?

Надеется, что это имеет смысл

+0

Email службы не должна быть в курсе таких услуг, как OrderService, вам нужен Посредник работать с обеими службами электронной почты && заказа, так что они будут развязаны – sll

+0

Что делает ваш сервис заказа делать? Просто инкапсулируйте OrderRepository? Или он делает больше, чем это, из внешнего вида вашего кода это просто похоже на избыточный уровень. – gideon

+0

Я задал бы другой вопрос. Хорошо ли выставлять IQueryable за пределами границы хранилища? Для меня нет. Это непрозрачная абстракция. –

ответ

2

Email служба не должна быть в курсе таких услуг, как 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) 
    { 
    } 
} 

Посредник:

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

Adapter:

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

+1

+1 Посредником я думал, что вы ссылаетесь на шаблон посредника в течение секунды, но это правильно и просто! :) Я не думаю, что требуется услуга заказа, но репозиторий уже позаботится о развязке **, как будут восстановлены заказы **? – gideon

+0

Привет, благодарю вас за отзыв. В приведенном выше примере, который вызовет метод SendEmails? Разве вам все равно не нужен код где-нибудь, который получает заказы, а затем вызывает службу электронной почты? – user1223218

+0

@ user1223218: это может быть посредник. Учитывая требования yu ahve, кто определяет, когда пришло время вызывать 'SendEmails()', это какой-то обработчик событий? – sll

0

То, что я сделал бы это

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

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

+0

Спасибо за ваш ответ, если служба электронной почты напрямую использует репозиторий, мы не рискуем разными действиями, которые могут произойти, поскольку GetAll() может быть изменен в сервисе для работы по-разному с GetAll() из репозитория. Спасибо – user1223218

2

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

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

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

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

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

подход DDD будет создать OrderNotificationService, который принимает событие домена OrderCreated и компонует сообщение, которое он посылает через Update EmailService

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

Пример кода:

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

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

Update2

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

Что касается заказа. Сохраните его, чтобы использовать репозиторий для сохранения всех строк заказа, самого заказа или чего-то еще, что было бы необходимо.

+0

Привет, спасибо за ваш ответ. Если в заказе должны быть такие методы, как Order.GetAll(), это не связано, как бы заказать, какой репозиторий использовать? Спасибо – user1223218

+0

+1 - OrderNotificationService определенно звучит скорее как правильный способ для меня. –

+0

@ user1223218: прочитайте мое обновление. – jgauffin

0

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

public class EmailService 
{ 
    private IOrderRepository _orderservice = null; 

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

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