Когда-то мой коллега сказал: «Монолит — это как большая квартира-студия: всё под рукой, но чтобы переставить диван, приходится выносить всю мебель». Микросервисы — это отдельные комнаты с замками. Звучит удобно, но если неправильно спроектировать, можно заблудиться в коридорах собственного кода. В этой статье я расскажу, как перевести Yii2-проект на микросервисные рельсы без боли и лишних жертв.
Почему монолит — это не всегда зло, но иногда хочется большего
Yii2 — замечательный фреймворк. Он даёт мощные инструменты для быстрой разработки. Но когда проект растёт, монолит начинает напоминать шкаф, куда запихнули всё: от носков до сварочного аппарата. Вроде всё работает, но найти нужную вещь — целый квест. Команда разработчиков упирается в конфликты в Git, тесты идут вечность, а малейшее изменение в одном модуле роняет всё приложение.
Микросервисы позволяют разбить систему на независимые части. Каждая часть — отдельный сервис со своей базой данных и логикой. Если упал сервис оплаты, каталог товаров продолжает работать. Команды могут разрабатывать свои сервисы независимо, используя разные технологии. Но за всё это приходится платить сложностью инфраструктуры и коммуникаций.
Прежде чем бежать дробить монолит, задайте себе вопрос: «А оно мне надо?». Микросервисы оправданы, когда проект действительно большой: много пользователей, много команд, высокие требования к масштабированию. Если у вас небольшой интернет-магазин с парой тысяч заказов в день — лучше остаться на монолите и не создавать себе проблем.
Как разделить монолит на микросервисы: практический подход
Есть два основных пути: «снизу вверх» и «сверху вниз». Первый — это когда вы уже имеете монолит и начинаете вырезать из него куски. Второй — проектируете систему сразу как набор сервисов. Для Yii2 чаще используют первый, потому что проекты уже существуют.
Начните с анализа бизнес-логики. Определите ограниченные контексты — это такие области, которые имеют чёткие границы. Например, в интернет-магазине: каталог, корзина, заказы, оплата, доставка, пользователи. Каждый контекст может стать отдельным микросервисом.
Важно понять, что сервисы должны общаться через чёткие интерфейсы — API. Внутри сервиса можно использовать любую архитектуру, но снаружи — только REST или GraphQL. И базы данных у каждого сервиса должны быть свои. Это критично: если два сервиса используют одну БД, они связаны намертво.
Вот пример, как можно разбить монолит на Yii2. Допустим, у нас есть приложение для управления задачами. Монолит содержит модули: пользователи, проекты, задачи, комментарии, уведомления. Можно выделить три сервиса:
- auth-service — отвечает за регистрацию, логин, выдачу токенов.
- project-service — управляет проектами и задачами.
- notification-service — отправляет уведомления.
Каждый сервис будет отдельным Yii2-приложением. Но можно и не ограничиваться Yii2 — например, notification-service может быть на Node.js, если это удобно. Главное — договориться о формате обмена данными.
Организация кода и структура проекта для микросервисов
Когда вы создаёте несколько Yii2-приложений, важно правильно организовать код. Не стоит копировать общие компоненты в каждый сервис — это приведёт к расхождению версий. Лучше вынести общий код в отдельные пакеты и подключать их через Composer.
Например, у вас есть библиотека для работы с JWT-токенами или для логирования. Создайте репозиторий для этой библиотеки и подключите её в каждый сервис. Так вы сможете обновлять общий код централизованно.
Вот пример структуры проекта с несколькими сервисами:
project-root/
├── services/
│ ├── auth/
│ │ ├── controllers/
│ │ ├── models/
│ │ └── config/
│ ├── project/
│ │ ├── controllers/
│ │ ├── models/
│ │ └── config/
│ └── notification/
│ ├── controllers/
│ ├── models/
│ └── config/
├── common/
│ ├── components/
│ ├── helpers/
│ └── messages/
└── composer.json
В каждом сервисе — своё приложение Yii2, со своими контроллерами, моделями и конфигами. Общие модули выносятся в common и подключаются через Composer.
Один из главных вопросов — как сервисы общаются между собой. Вариантов несколько: HTTP-запросы, очереди сообщений (RabbitMQ, Kafka) или gRPC. Для Yii2 чаще всего используют HTTP-запросы, потому что это проще. Но если нужна высокая производительность, лучше использовать очереди.
Пример общения через REST API между двумя сервисами:
use yii\httpclient\Client;
$client = new Client();
$response = $client->createRequest()
->setMethod('GET')
->setUrl('http://project-service/api/projects')
->setHeaders(['Authorization' => 'Bearer ' . $accessToken])
->send();
if ($response->isOk) {
$projects = $response->getData();
}
А вот так может выглядеть контроллер в сервисе проектов, который принимает запрос от другого сервиса:
namespace app\controllers;
use yii\rest\ActiveController;
class ProjectController extends ActiveController
{
public $modelClass = 'app\models\Project';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => \yii\filters\auth\HttpBearerAuth::class,
];
return $behaviors;
}
public function actionIndex()
{
// Логика получения списка проектов
}
}
Управление базами данных и миграциями
Каждый микросервис должен иметь собственную базу данных. Это принципиально. Если сервисы используют одну БД, то они уже не микросервисы, а просто модули монолита. Разделение баз данных даёт независимость: вы можете масштабировать только те сервисы, которые нуждаются в этом, и не бояться конфликтов при миграциях.
Но тут есть нюанс: как поддерживать целостность данных между сервисами? Например, заказ ссылается на пользователя. Но пользователь живёт в другой базе. Ответ — через API. Сервис заказов не должен напрямую обращаться к базе пользователей, он должен запрашивать данные через API сервиса пользователей.
Для миграций в Yii2 есть удобный инструмент — yii migrate. Но в микросервисах нужно запускать миграции для каждого сервиса отдельно. Вы можете использовать общие миграции, но лучше, чтобы каждый сервис имел свои миграции, которые касаются только его базы.
Пример команды для запуска миграций в конкретном сервисе:
cd services/auth
php yii migrate
Если вам нужно выполнить миграции для всех сервисов сразу, можно написать скрипт:
#!/bin/bash
for service in services/*; do
if [ -f "$service/yii" ]; then
echo "Running migrations for $service"
(cd "$service" && php yii migrate --interactive=0)
fi
done
Важно: никогда не используйте общие таблицы между сервисами. Если вам нужны данные из другого сервиса, обращайтесь к его API. Это правило нарушать нельзя.
Советы по развертыванию и мониторингу
Когда у вас много микросервисов, развертывание вручную превращается в ад. Нужно использовать Docker и оркестраторы типа Kubernetes. Но это отдельная большая тема. Если вы только начинаете, можно использовать Docker Compose для локальной разработки.
Вот пример docker-compose.yml для трёх сервисов:
version: '3'
services:
auth:
build: ./services/auth
ports:
- "8081:80"
environment:
- DB_HOST=auth-db
project:
build: ./services/project
ports:
- "8082:80"
environment:
- DB_HOST=project-db
notification:
build: ./services/notification
ports:
- "8083:80"
environment:
- DB_HOST=notification-db
auth-db:
image: mysql:8
environment:
- MYSQL_ROOT_PASSWORD=secret
project-db:
image: mysql:8
environment:
- MYSQL_ROOT_PASSWORD=secret
notification-db:
image: mysql:8
environment:
- MYSQL_ROOT_PASSWORD=secret
Мониторинг в микросервисах — это критично. Когда у вас 10 сервисов, вы не можете заходить на каждый и смотреть логи. Нужна централизованная система логирования и метрик. Подойдут такие инструменты как ELK (Elasticsearch, Logstash, Kibana) или Grafana + Prometheus.
В Yii2 можно настроить отправку логов в центральное хранилище. Например, используя компонент log с целевым назначением в Kafka:
'log' => [
'targets' => [
[
'class' => 'yii\log\KafkaTarget',
'topics' => ['app-logs'],
'brokers' => 'kafka:9092',
],
],
],
Но такой компонент не входит в стандартную поставку Yii2, его нужно написать самостоятельно или найти готовое расширение.
Также не забывайте про трейсинг запросов. Когда запрос проходит через несколько сервисов, сложно понять, где он завис. Используйте OpenTracing или Jaeger.
Что в итоге
Микросервисы на Yii2 — это реально, но требует дисциплины. Вы не сможете просто взять и переписать монолит за вечер. Нужно тщательно спроектировать границы сервисов, настроить общение и инфраструктуру. Зато потом вы получите систему, которую легко масштабировать и развивать независимыми командами.
Если ваш проект небольшой — не геройствуйте, оставайтесь на монолите. А если вы решились, начните с выделения одного-двух сервисов, которые действительно узкие места. Постепенно вы сможете перевести всё на микросервисную архитектуру.
Помните: микросервисы — это не серебряная пуля. Это инструмент, который требует уважения. Используйте его с умом, и тогда ваш код будет радовать вас и ваших коллег.
