Статьи

Создание больших магистральных приложений

Магистраль дает структуру веб-приложениям, но часто этого недостаточно. Много вопросов возникает, когда сложность растет. Как мы управляем взаимодействиями пользовательского интерфейса? Что мы делаем с вариантами использования домена? Как насчет взаимодействия клиент / сервер? Наконец, как мы все связываем? В этой статье рассказывается об архитектуре, которая может ответить на все эти вопросы.

Что такое хорошая архитектура?

Прежде чем мы перейдем к решению, давайте посмотрим, что составляет хорошую архитектуру:

  1. Доменная логика приложения отделена от механизма доставки. Другими словами, логика домена не зависит от уровня пользовательского интерфейса или серверной части.

  2. Инициализация приложения отделена от его выполнения. Есть только одно место, где все компоненты созданы и подключены.

  3. Приложение состоит из множества мелких компонентов, каждый из которых выполняет только одну функцию. То есть каждый компонент придерживается принципа единой ответственности.

  4. Координация и расчеты раздельные. Это, наверное, самый фундаментальный принцип разработки программного обеспечения. Это говорит о том, что объект либо принимает решения, либо выполняет чужие решения.

  5. Все взаимодействия и варианты использования являются явными. Поведение не проявляется, а явно определяется в коде. Если существует критерий приемлемости, описывающий какое-либо взаимодействие с пользовательским интерфейсом, то должен быть файл и объект, отвечающий за выполнение этого взаимодействия. Точно так же, если есть сценарий использования, описывающий некоторое взаимодействие с доменом, должен существовать объект, выполняющий его.

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

обзор

Теперь, определив желаемые свойства архитектуры, давайте рассмотрим следующий дизайн.

Архитектура

В нем довольно много частей:

  • Контролирующий докладчик координирует взаимодействие с пользовательским интерфейсом.
  • Представление касается DOM. У него нет логики, кроме настройки привязок данных.
  • Служба вариантов использования только координирует выполнение вариантов использования.
  • Доменная служба и сущность содержат независимую от контекста бизнес-логику приложения.
  • Репозиторий (а также хранилище, сериализатор и десериализатор) инкапсулирует взаимодействия с серверной частью.

Давайте рассмотрим каждый компонент, начиная с ведущего докладчика.

Ведущий ведущий

Наблюдающий образец презентатора используется, чтобы организовать сложные взаимодействия пользовательского интерфейса. Чтобы понять, почему мы хотели бы использовать его, давайте рассмотрим стандартный способ реализации логики представления в приложении Backbone:

Логика презентации

Вид здесь знает и многое делает. Во-первых, он слушает модель, а когда она меняется, он повторно отображает шаблон. Во-вторых, он отвечает за обработку пользовательского ввода. Кроме того, представление часто отвечает за выполнение проверок и сохранение модели. Совершенно очевидно, что в сложных взаимодействиях представление склонно накапливать много обязанностей и его трудно поддерживать. Кроме того, он тесно связан с шаблоном, что затрудняет модульное тестирование.

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

Ведущий ведущий

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

Узнайте больше о руководящем образце презентатора.

Использование Case Service

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

вместилище

Несмотря на то, что все больше и больше поведения передается клиенту, в какой-то момент вам придется общаться с сервером. Обойти это невозможно. Такое общение часто бывает довольно сложным и, следовательно, не должно осуществляться субъектами. Вместо этого он должен обрабатываться назначенным объектом — хранилищем.

Типичное взаимодействие с хранилищем выглядит следующим образом:

вместилище

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

Почему четыре объекта вместо одного?

Почему бы не поместить все обязанности в хранилище? Зачем нам сериализатор и хранилище?

  • Во-первых, это отделяет координацию от фактической работы. Хранилище — это координатор, принимающий решения о том, какие действия хранилища вызывать и что делать с возвращенными обещаниями. Хранилище, сериализатор, десериализатор выполняют реальную работу и не координируют действия.

  • Во-вторых, такая схема упрощает модульное тестирование. Хранилище может быть протестировано изолированно с заглушенными сериализаторами и хранилищами. Сериализатор и десериализатор — это преобразования данных без сохранения состояния, и, следовательно, их легко протестировать. Хранение не должно быть проверено вообще.

Подводя итоги:

  • Хранилище инкапсулирует всю связь с сервером.
  • API хранилища является объектно-ориентированным.
  • Хранилище, с другой стороны, ориентировано на данные.
  • Сериализатор / десериализатор преобразует объекты в данные и наоборот.
  • Хранилище может быть украшено или даже заменено, не затрагивая хранилище. Примером может быть добавление кэширования или использование поддельного хранилища для тестирования.
  • Нет необходимости определять настраиваемое хранилище или сериализатор для каждого репозитория, если вы можете использовать общие, которые основаны на соглашении.

пример

Следующий пример иллюстрирует взаимодействие между руководящим презентатором, службой вариантов использования и хранилищем.

var ProductUpdateService = function (orderRepository) {
  this.update = function (product, listener) {
    if(!product.valid()){
      listener.productUpdateFailed(product);
    }
    orderRepository.save(product).done(listener.successfulProductUpdate)
  };
};

// All the dependencies are injected and, therefore, can be mocked up.
var ProductUpdatePresenter = function (productUpdateService, productUpdateForm) {
  this.updateProduct = function (product) {
    // Does not contain any domain logic. Delegates to the use case service instead.
    // Notice that we are passing the presenter into the use case service.
    // The service will notify the presenter about the results of the use case execution.
    // The presenter can react (for instance, close some sort of dialog).
    productUpdateService.update(product, this);
  };

  // These methods will be called by the use case service
  this.successfulProductUpdate = function (product) {/*...*/};
  this.productUpdateFailed = function (product){/*...*/};

  _.bindAll(this);
  productUpdateForm.on('update', this.updateProduct);
};

инициализация

Изучив все компоненты, давайте посмотрим, как их можно создать и подключить.

//app.js
var orderRepository = new OrderRepository();
var productUpdateService = new ProductUpdateService(orderRepository);
var productUpdateForm = new ProductUpdateForm();
var productUpdatePresenter = new ProductUpdatePresenter(productUpdateService, productUpdateForm);

Сначала создайте все компоненты в одном месте, например, в файле с именем app.js. Постарайтесь сделать этот файл максимально простым (например, операторы if). Во-вторых, избегайте использования глобальных переменных и вместо этого вводите все зависимости. Если это становится слишком утомительным и многословным, используйте шаблон поиска сервисов.

Проходя через цели

  1. Доменная логика приложения отделена от механизма доставки.

    Описанный дизайн делает это разделение явным. Доменная логика приложения представлена ​​сервисами прецедентов, доменными сервисами и объектами. Все остальное — это механизм доставки.

  2. Инициализация приложения отделена от его выполнения.

    Есть одно место, app.jsгде все компоненты созданы и подключены.

  3. Приложение состоит из множества мелких компонентов, каждый из которых выполняет только одну функцию.

    • Контролирующий докладчик координирует взаимодействие с пользовательским интерфейсом.
    • Представление — это виджет, который абстрагируется от DOM.
    • Служба вариантов использования управляет взаимодействием домена.
    • Служба домена выполняет некоторые вычисления, связанные с доменом.
    • Доменная сущность хранит некоторое состояние.
    • Хранилище координирует взаимодействие клиент / сервер.
    • Хранилище делает запросы AJAX.
    • Сериализаторы / десериализаторы преобразуют объекты в данные и наоборот.
  4. Координация и расчеты раздельные.

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

  5. Все взаимодействия и варианты использования являются явными.

    Для каждого взаимодействия пользовательского интерфейса есть только один ведущий. Аналогичным образом, существует один и только один сервис вариантов использования для каждого варианта использования.

  6. Компоненты зависят от протоколов, а не от конкретных реализаций.

    Внедрение зависимостей позволяет это.

Что если в моем приложении нет бизнес-логики?

Некоторые приложения имеют небольшую сложность домена. Если это так, вы можете упростить проект, объединив сценарии использования и доменные службы с ведущим докладчиком.

Архитектура.  Нет домена

Завершение

Магистраль дает структуру веб-приложениям, но часто этого недостаточно. В этой статье я показал:

  • Как управлять взаимодействиями пользовательского интерфейса, используя шаблон ведущего докладчика
  • Как представлять варианты использования с использованием шаблона службы вариантов использования
  • Как инкапсулировать связь клиент / сервер с использованием репозитория
  • И как все соединить с помощью внедрения зависимостей

Прочитайте больше

Узнайте больше об архитектуре на сайте engineering.nulogy.com .