Универсальный способ внедрения зависимостей.
Внедрение зависимостей это не только код, но и пакетный менеджер, наличие запущенных нужных сервисов (db, minio, kafka и тд), различные extension (pdo, mbstring, sockets и тд) и установленный софт и системные библиотеки.
У команды есть свои компетенции (экспертиза) и предпочтения.
Если команда работала с очередями в kafka (pull модель), им потребуется время для переключения на rabbitmq (push модель), mysql или postgres и так далее.
Команды могут выбирать технологии даже если отсутствует компетенция, но есть желание ее получить.
Специфика проекта индивидуальна, если вы строите OLAP систему с аналитическими запросами, использовать ORM не лучшая идея даже при наличии компетенций в ORM.
Все эти требования со временем могут меняться, например проект начинался с memcached, потом перешли на redis, потом перешли на dragonfly или valkey и тд.
Эти изменения постоянный процесс развития проекта и необходимо предоставить возможность делать это удобно и предсказуемо.
Конкретные примеры внедрений зависимостей, для примера:
- Простейший случай установки зависимости (guzzlehttp/guzzle).
- Использование абстракции и регистрация реализации(psr/http-client).
- Зависимости с дополнительными сервисами (postgres).
- Использование
Module(migration для db).
Это универсальный алгоритм следуя которыми можно внедрить любую зависимость:
- Определяемся с целями.
- Настрайваем docker image.
- Настрайваем сервисы для разработки.
- Обновляем окружение разработки и запускаем его
- Устанавливаем необходимые пакеты.
- Подключаем новые модули или настрайваемые существующие.
- Используем зависимости в своем коде.
- Настрайваем
App\Module. - build или restart окружения.
- Проверяем результат.
В зависимости от ситуации, некоторые пункты могут быть пропущены.
Подробней о некоторых пунктах:
1. Определяемся с целями.
Вы определяетесь с тем что вам необходимо и каких версий, как это будет на этапе разработки или в production.
Во время разработки можно запускать свои сервисы (db, minio и тд), а можно по сети подключаться к серверам разработки расположенным в интернете (s3, db и тд), по возможности предпочитайте первый вариант.
Как приложение будет в production режиме работать, с какими сервисами будет взаимодействовать, старайтесь чтобы версии совпадали и тд.
2. Настрайваем docker image.
Основная работа с файлом Dockerfile в котором перечислены различные images.
base image будет установлено во все другие image, например там можно установить php extension (sockets, pdo и тд) которые используются в других окружениях. Можно установить пакеты (библиотеки) для alpine которые могут понадобиться вашему приложению.
dev image то что нужно на этапе разработки, это может быть пакетный менеджер composer, xdebug extension, pcov extension, make и другие вещи полезные в разработке.
prod image здесь можно настроить инструменты в production, обновить конфигурационные файлы для production, включить opcache, настроить логирование в json и тд.
3. Настрайваем сервисы для разработки.
Основная работа с docker-compose.yml, сервисы запускаем у разработчика на устройстве.
Например это могут быть: postgres, mysql, minio(s3), redis, memcached, opensearch, clickhouse, kafka или rabbitmq и тд.
Помимо указания самих сервисов, здесь можно их настроить (логины и пароли), а также указать зависимости между ними.
4. Обновляем окружение разработки и запускаем его.
Обновляем окружение разработки (останавливаем если запущенно, удаляем то что было, собираем актуальную версию)
make refresh
Запускаем собранную версию.
make dev
5. Устанавливаем необходимые пакеты.
Используя composer управляем пакетами
Делаем все команды внутри app shell, чтобы окружения было одинаковым.
make shell
Устанавливаем пакет для разработки и production.
composer require vendor/package[:version]
Устанавливаем пакет только для разработки
composer require --dev vendor/package[:version]
6. Подключаем новые модули или настрайваемые существующие.
Основная работа с src/Application.php.
После установки пакетов, могут быть Module которые помогают с конфигурацией DI и выполнением необходимой интеграцией
в ваше приложение.
Например, поиск всех классов реализующих интерфейсов (вне зависимости от их расположения в директориях), работа с php attribute (поиск http обработчиков, middlewares, cli commands, migrations и тд).
Некоторые модули могут иметь как обязательные аргументы которые потребуется задавать, например App\Module
зависит от текущих переменных окружения.
Некоторые модули могут иметь опциональные атрибуты, с помощью которых можно делать часто встречающиеся действия.
Например, переопределить обработчик страницы 404, возможность зарегистрировать произвольные CLI command, указать http
middlewares для всего приложения и тд.
7. Используем зависимости в своем коде.
Внедряем зависимости в свой код с помощью конструктора и используем их в прикладных целях.
8. Настрайваем App\Module.
Основная работа с src/Module.php.
Конфигурируем зависимости, задаем значения параметров обязательных или опциональных.
Оставляем возможность изменить параметры с помощью переменных окружений (environment) для devops.
Регистрируем имплементации для интерфейсов.
Управляем жизненным циклом зависимостей (scoped, singleton, factory).
9. build или restart окружения.
build
make build
В некоторых случаях build может быть неудачным, когда зависимости не настроены.
Например, не переданные обязательные поля или незарегистрированы интерфейсы, или абстрактные классы.
В сообщение с ошибкой указано какая именно зависимость не может быть разрешена и цепочка зависимостей.
restart
make restart
Останавливает текущую dev версию, запускает снова (build + запуск).
10. Проверяем результат.
Проверяем что все корректно работает в вашем коде, в обработчиках или консольных командах.
Полезные команды можно выносить в Makefile или composer scripts.
Makefile команды запускаются с хостовой системы.
composer scripts запускаются из shell app.
Important
Однажды настроенная зависимость может быть переиспользована в любом месте.