- Корпоративные приложения имеют
- Несогласованную бизнес-логику, ее может быть много и она может содержать много частных случаев.
- Большое количество интеграций с другими приложениями.
- Данные могут храниться десятки лет.
- Конкурентный доступ к данным.
- Разные представления одних и тех же данных.
- Сотни, тысячи и более (в вебе) пользователей.
- Превращайте большие проекты в малые, их проще понимать.
- Нет двух одинаковых корпоративных приложений: для каждого из них нужно рассматривать свои ограничения и выбирать из нескольких альтернатив решения.
- В некоторых приложениях клиентская логика будет простой и достаточно использовать обычный MVC, а в других потребуется SPA.
- Производительность
- Время ответа (сервера)
- Отзывчивость интерфейса
- Пропускная способность (например, количество транзакций в секунду)
- Чувствительность к загрузке
- Типовые решения могут быть взаимозаменяемы. Тогда можно подумать о том, какое решение может НЕ подойти, из возможных.
- Типовое решение не панацея: в каждой конкретной ситуации нужно думать о плюсах и минусах, получится что-то своё.
- Слой - это набор абстракций одного уровня.
- Если слои не соприкасаются (как в пироге), то обычно они не знают друг о друге. Это позволит заменять или изменять слой, не затрагивая все слои.
- Пример, стек протоколов TCP/IP.
- Если нужно добавить одно поле в базу, то может потребоваться прокинуть его через все слои вплоть до UI.
- Представление/домен/источник данных.
- Функции представления не должны вызываться из домена или источника данных.
- Альтернатива: шестигранная архитектура, где система является ядром и стыкуется через интерфейсы с внешними системами.
- Мы хотим выделить красным цветом товары, продажи которых выросли на 10% и больше. Эту логику можно держать как в домене, так и в представлении. Выбрать сложно.
- Катализаторы сложности
- Распределенные системы обработки и хранения данных
- Многопоточность с общим состоянием
- Сочетание плохо стыкуемых концепций: реляционной и объектной моделей
- Переносимость
- Высокая производительность
- Физически слои могут функционировать на разных уровнях, в зависимости от требований к быстродействию и отзывчивости.
- 3 подхода к организации бизнес-логики
- Сценарий транзакции:
- Набор процедур без объектов. Процедура принимает данные от представления, выполняет обработку путём вызова других процедур и возвращает данные в представление.
- Модель предметной области:
- Выделяются сущности, для них создаются классы.
- Внутрь классов прячется логика, а наружу выставляется только интерфейс.
- Модуль таблицы:
- Похож на сценарий транзакции, но отличается тем, что функции спрятаны в отдельный модуль, предоставляющий доступ к конкретной таблице.
- Идентификатор объекта передаётся во все функции.
- Сценарий транзакции:
- Все 3 подхода можно использовать совместно, только для разных частей приложения.
- Слой служб - это промежуточный интерфейс между представлением и доменной логикой. Тут можно ограничить доступ, выполнять вызовы моделей или даже реализовать сценарий транзакции прямо в этом коде. Слой служб опционален.
- Active record: у объекта-записи есть методы save, destroy.
- Data mapper: объекты простые, а методы сохранения и извлечения находятся в отдельном объекте-репозитории.
- Unit of work: хранит информацию об изменениях объектов и сохраняет их все, а у самих объектов нет методов save или delete.
- Unit of work часто реализуется самостоятельно в слое бизнес-логики, что не очень удобно для больших и сложных проектов.
- Используй finders: findByPk(id), findByCustomer(customer), etc.
- TypeORM (data mapper/active record) vs Sequelize (active record).
- Входной контроллер/бизнес-логика/представление. НЕ то же самое, что и MVC.
- Представление по шаблону против Представления с преобразованием. Последнее удобно, если в приложении есть сложная логика выбора экранов и нужен промежуточный логический экран, общий для всех представлений.
- Если итоговые представления слишком сильно отличаются (десктоп и веб), то иметь логическое представление нецелесообразно.
- Проблемы параллелизма
- При записи. Потерянные изменения - при обновлении одних и тех же данных без атомарной проверки их неизменности перед сохранением.
- При чтении. Несогласованное чтение - если данные могут измениться в процессе чтения и мы видим это изменение.
- Приходится выбирать между корректностью и параллелизмом.
- В главе много деталей про блокировки, транзакции, уровни изоляции. Если пишешь много кода, работающего с реляционной базой, читай обязательно.
- Хранить данные сеанса на клиенте
- Хранить данные сеанса на сервере (память)
- Хранить данные сеанса в персистентном хранилище
- Интерфейсы удаленного вызова должны быть как можно более общими, чтобы сделать как можно больше работы за один сетевой вызов.
- Интерфейсы локального вызова могут быть очень специфическими, детальными.
- Сценарий транзакции
- Состоит из последовательности вызова процедур.
- Естественно реализовать исключительно в функциональном стиле.
- Доменная модель
- Создаётся набор объектов. Бизнес-логика раскладывается по отдельным сценариям так, что каждому сценарию соответствует свой объект.
- С помощью фабрики создаются объекты со специфическим поведением (оно скрыто за общим интерфейсом).
- Table data gateway
- По-сути обертки над строковыми запросами к базе, с динамическим приведением типов возвращаемых полей.
- Похоже на то как я сейчас работаю с Salesforce.
- Active record
- В статических методах лежат методы поиска. Они возвращают активные записи, то есть записи, методы которых модифицируют базу данных.
- Удобно использовать для простых вставок, удалений и обновлений и для довольно сложных запросов.
- Sequelize-реализация читается гораздо лучше, чем пример в книге на ADO.NET.
- Data mapper
- Чистые модели, без методов работы с базой.
- Работа с базой вынесена в Repository - отдельный объект, со всеми CRUD-методами.
- Можно реализовать более сложную логику, чем в Active record.
- Смотри реализацию в TypeORM: она читаемее, чем то, что приведено в книге.
- Отделяй модель (бизнес-логику) от представления информации. Контроллер может быть не отделён от представления или заниматься только маршрутизацией.
- Gateway (Шлюз)
- Предоставляет более удобный интерфейс к внешнему сервису.
- Легко подменить фиктивной службой (Service Stub) для тестирования (внешний сервис может быть недоступен и т.п.).
- Похож на паттерн Фасад (Facade).
- Mapper (Преобразователь)
- Соединяет две системы таким образом, что эти системы ничего не знают друг о друге.
- В отличии от Медиатора в Преобразователе объекты не знают и о существовании Преобразователя.
- Реализация в виде Observer или Data Mapper.
- Registry (Реестр)
- Глобальный объект, у которого можно запросить объект.
- Лучше не использовать, но иногда он удобен, особенно в маленьком проекте.
- Special Case (Особый случай)
- Вместо возвращения null вернуть объект с поддержкой тех же методов.
- Станет намного удобнее пользоваться полиморфным поведением.
- Plugin
- Добавляет возможности программе во время выполнения на основе интерфейса плагина.