Архитектура S # arp: Как организовать сервисы приложений - PullRequest
1 голос
/ 06 марта 2011

С S # arp Architecture я понимаю, что логика домена (иначе говоря, бизнес-логика), которая работает с несколькими типами объектов, лучше всего обрабатывается уровнем Application Services.

Таким образом, классам в Application Services потребуется доступ к репозиториям. Предположительно, вы вводите репозитории через конструкторы. Поскольку для каждого типа сущности существует один класс репозитория, любая достаточно реалистичная задача будет нуждаться в доступе к нескольким репозиториям. Таким образом, у вас может быть класс Application Services, который выглядит следующим образом:

public class DogTasks
{
    public DogTasks(IRepository<Dog> dogRepository, 
            IRepository<Trick> trickRepository,
            IRepository<DogTrick> dogTrickRepository,
            IRepository<Lesson> lessonRepository)
    {
        // etc
    }

    public void TeachDogNewTrickAtALesson(int dogID, string trickName, int lessonID)
    {
        // etc
    }

    // other methods, etc

}

Этот класс Tasks затем может быть введен в соответствующий контроллер.

Пока, я думаю, я понял. Но меня беспокоит следующее:

  • Когда мне нужен новый метод Application Services, использующий комбинацию репозиториев, которой у меня еще нет, мне приходится выбирать между изменением конструктора для одного из моих существующих классов для принятия новых репозиториев или запуском новый класс в целом. Добавление аргументов в конструкторы расстраивает многие юнит-тесты, но распространение новых классов тоже нехорошо.

  • Когда контроллерам необходимо выполнять простые операции с репозиторием (например, get), имеет смысл внедрить репозитории в контроллеры, а также в классы служб приложений. Но затем я получаю ту же проблему «изменения аргументов конструктора». Другая альтернатива, по-видимому, заключается в том, чтобы позволить слою Application Services играть только с репозиториями, но затем вы добавляете много стандартного кода, добавленного к Application Services, чтобы выполнять очень простые вещи.

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

например. У вас есть много классов, которые выполняют только одно задание? Или вы объединяете связанные задачи вдоль линий сущностей? Как вы справляетесь с задачами, которые требуют много репозиториев? Означает ли необходимость в большом количестве репозиториев для задания время вернуться к чертежной доске?

Ответы [ 2 ]

4 голосов
/ 06 марта 2011

Во-первых, я хотел бы опровергнуть ваше предположение, что каждому объекту нужен свой собственный репозиторий.Пер, Эрик Эванс, «Доменно-управляемый дизайн»

Хранилища предоставляют доступ к выбранным совокупным корням.Репозитории запрещены изнутри совокупности.

Учитывая ваш пример, у собаки есть ряд приемов, которые она выучила.Если вы хотите добавить новый трюк к собаке, вы должны сделать что-то вроде этого:

var dog = dogRepository.Get(dogId);
dog.Tricks.Add(newTrick);
dogRepository.SaveOrUpdate(dog);

Когда мне нужен новый метод Application Services, который использует комбинацию репозиториев, которые я не используюпока нет,

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

Другая альтернатива, похоже, состоит в том, чтобы позволить слою Application Services играть только сРепозитории, но затем вы получаете много стандартного кода, добавляемого к службам приложений для выполнения очень простых задач.

Контроллеры контролируют.Думайте о контроллерах как о части пользовательского интерфейса, они перемещают вас от страницы к странице.Я признаю, что для простых вещей кажется проще внедрить репозиторий в контроллер, но когда ваш проект растет, разделение очень поможет, особенно если вы в конечном итоге получите еще один хук приложения на свой уровень задач.Храните репозитории вне контроллеров.

Например, у вас есть много классов, которые выполняют только одну задачу каждый?Или вы объединяете связанные задачи вдоль линий сущностей?Как вы справляетесь с задачами, которые требуют много репозиториев?Означает ли необходимость в большом количестве репозиториев для задания время, чтобы вернуться к чертежной доске?

Опять же, я думаю, что это восходит к определению совокупных корней.Наличие 4-5 репозиториев в задаче не так уж сложно.Я обычно организую свои задачи по тому, что пытается сделать приложение, с мыслью, что если пользовательский интерфейс изменится, скажем, на внешний JSON-запрос, вам просто нужно вызвать правильную задачу.

Надеюсь, что это ответит на вашвопрос.Не стесняйтесь публиковать это в списке рассылки Sharp, вы можете получить лучший ответ там.

Редактировать на основе комментариев:

Посмотрите, кто может мне помочь (https://github.com/sharparchitecture/Who-Can-Help-Me) для примеракак использовать слой ApplicationServices / Tasks. У них довольно маленькая модель предметной области, поэтому у каждого объекта есть своя задача.

Я думаю, что вы немного путаете терминологию, или, возможно, я неясен. ИдеяЗа уровнем ApplicationServices лежит дальнейшее абстрагирование пользовательского интерфейса от уровня домена. Репозитории являются объектами уровня домена, и знания о них не должны находиться в контроллере. Если вы в конечном итоге поменяли ORM или даже перешли на систему хранения на основе документов,вы поймете, почему эта абстракция делает ее действительно удобной, вам просто нужно убедиться, что ваши контракты ApplicationServices работают и не нужно портить контроллеры.

Но не путайте необходимость вApplicationServices как способ проверки будущего. Он просто обеспечивает дальнейшее разделение между вашими клиентами.и разделение почти всегда хорошо.

Опять же, для проекта, над которым вы работаете в одиночку, все это может показаться излишним.Когда вы работаете с другими разработчиками, вся эта абстракция действительно хороша.У вас может быть команда, работающая над проблемами домена верхнего уровня, и команда, работающая над уровнем представления, и у вас будет хорошее разделение интересов.

1 голос
/ 06 марта 2011

Слышали ли вы о шаблоне абстрактной фабрики?Это решает эту проблему хорошим способом:

public interface IDalFactory
{
  // One way
  IRepository<Trick> TrickRepository { get; }
  IRepository<Dog> DogRepository { get; }
  ...

  // Other way
  IRepository<T> GetRepository<T>();
}

public DogTasks
{
  public DogTasks(IDalFactory dalFactory)
  {
    ...
  }
}

Это зависит от вас, как вы реализуете IDalFacotry.Я обычно использую ленивую инициализацию репозиториев.После создания хранилища оно хранится и используется повторно.Для каждого запроса http создается один экземпляр фабрики.

Недостатки в том, что вы не можете контролировать фабрики, работающие с вашей службой приложений.Но это ваш выбор.Добавление новых репозиториев в конструктор или использование фабрики.

...