Шаг 2: шлюз таблицы
Все SQL-строки для
ordersуезжают в классOrdersTable, контроллер зовёт его методы, но всё ещё работает с рядами.
Урок: Выделение репозитория. Предыдущий шаг: SQL прямо в контроллере. Следующий: репозиторий в домене.
Что было
Контроллер писал SQL сам: один и тот же SELECT по id дважды, имена колонок в каждом методе, правило отмены между запросами.
Что меняем
Выделяем шлюз таблицы (Table Data Gateway): OrdersTable знает таблицу orders и только её. У него четыре метода по числу запросов: find(id) возвращает ряд или пусто, findByCustomer, insert сам берёт следующий id и возвращает его, updateStatus. Каждая SQL-строка теперь написана ровно один раз.
Контроллер получает OrdersTable в конструкторе вместо Database. Логика методов не меняется: те же проверки, те же ответы, только вместо query('SELECT ...') вызов find. Тесты не меняются вовсе, кроме строки сборки: new OrdersController(new OrdersTable(new Database())).
Это механический шаг, его можно делать почти без риска: переносим запрос в метод шлюза, заменяем вызов, гоняем тесты.
Как это собирается
Цепочка из трёх объектов: база, шлюз над ней, контроллер над шлюзом. В приложении её собирает контейнер, роутер только вызывает действия. Сценарий тот же, что на первом шаге, и вывод совпадает строка в строку.
Сценарий целиком с выводом: пример на PHP, он же на Go, Python и TypeScript в панели кода.
Что стало лучше
- SQL в одном месте. Переименовать колонку или добавить запрос - правка одного класса.
- Методы контроллера стали короче, и правило отмены стало заметнее: между
findиupdateStatusостались только проверки. - Шлюз можно проверить отдельно от HTTP, а контроллер больше не знает текста запросов.
Что ещё болит
- Контроллер по-прежнему работает с рядами:
$row['status'],row["total_cents"]. Он знает имена колонок, а в Go всё так же приводитanyк строке. - Правило отмены всё ещё в контроллере. Шлюз - это таблица, а не заказ, ему правила не положены.
OrdersTable- конкретный класс над конкретнойDatabase. Подменить его в тесте можно только наследованием или моком, и контроллер по-прежнему тестируется вместе с базой.
Запуск: make test T=lessons/002-extract-repository/step-2