Архитектура S # arp: как организовать службы приложений

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

Таким образом, классам в 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, чтобы делать очень простые вещи.

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

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


person codeulike    schedule 06.03.2011    source источник


Ответы (2)


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

person kertosis    schedule 06.03.2011
comment
Я на 100% согласен с вашими комментариями, и я также скажу, что у вас могут быть некоторые службы, которые внедряются в другие, которые решают дополнительные функции, такие как отправка электронных писем или что-то в этом роде, и я считаю, что хорошо оставлять контроллер как можно меньше с взаимодействиями только с сервисы, которые инкапсулируют всю логику. - person Homer1980ar; 07.03.2011
comment
Спасибо за напоминание о совокупных корнях. в моем случае я, кажется, связываю вместе множество совокупных корней (скажем, добавляю трюк к собаке, но также записываю Урок, в котором она его усвоила, где Урок - еще один совокупный корень). Ответ Ласислава об абстрактной фабрике может мне помочь. Хотя я понимаю, что это в основном означает, что «все службы приложений могут получить доступ к любому репозиторию», что некоторые могут осудить. - person codeulike; 07.03.2011
comment
Я доволен идеей, что службы приложений (и основные объекты домена под ними) должны инкапсулировать всю логику, но мне не удалось найти никаких хороших примеров того, как должны выглядеть службы приложений (например, один класс на задачу или один класс на объект?), и я скептически отношусь к слоям ради слоев. В настоящий момент я делаю один класс для каждого совокупного корня (_1 _, _ 2_), но мне кажется, что я в опасности в основном перестроить объекты домена в службах приложений только потому, что службам приложений разрешено использовать репозитории, а объекты домена не . - person codeulike; 07.03.2011
comment
Короче говоря, я думаю, что ищу хороший пример того, что Application Services должны и чего не должны делать. Если объекты домена не должны использовать репозитории, но это нормально для служб приложений, почему это так и каковы последствия для структуры классов в службах приложений. - person codeulike; 07.03.2011
comment
Добавлено приложение, надеюсь, оно проясняет ситуацию. Я бы добавил, что если проект настолько мал, что это действительно сбивает с толку, не беспокойтесь об этом. Я очень рекомендую вышеупомянутую книгу Domain Driven Design, а также книгу Лармана «Применение UML и шаблонов», которая не похожа на книгу DDD, но на самом деле охватывает многие из этих тем. - person kertosis; 07.03.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-запроса создается один экземпляр фабрики.

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

person Ladislav Mrnka    schedule 06.03.2011