🏗 Laravel SaaS: как не утонуть в собственном коде через полгода
Дефолтная структура Laravel отлично заходит на старте. Потом приходят мультитенантность, биллинг, сложные правила. В итоге контроллеры пухнут, логика расползается по моделям и джобам.
Рабочий рецепт: модульный монолит с доменной организацией. Каждый домен — мини-приложение внутри приложения.
💡 Правила, которые реально экономят часы на рефакторинге:
— контроллер принимает FormRequest, дёргает сервис, возвращает Resource;
— бизнес-логика живёт в сервисах, чтобы переиспользоваться в API, CLI и очередях;
— доступ к БД — через репозитории, без Booking::where() в сервисах;
— сразу версионируй API (/api/v1), иначе первый же breaking change принесёт боль.
По SaaS-специфике: Sanctum для SPA, Cashier + Stripe для подписок (писать биллинг руками — плохая идея), stancl/tenancy если нужна изоляция тенантов. Тяжёлое отправляем в очереди на Redis, дебаг с Telescope локально и Sentry в проде.
🔗 Читать оригинал
Библиотека пхпшника
#book_code
Дефолтная структура Laravel отлично заходит на старте. Потом приходят мультитенантность, биллинг, сложные правила. В итоге контроллеры пухнут, логика расползается по моделям и джобам.
Рабочий рецепт: модульный монолит с доменной организацией. Каждый домен — мини-приложение внутри приложения.
app/Domains/Bookings/
Models/
Services/
Repositories/
DTOs/
Actions/
Requests/
💡 Правила, которые реально экономят часы на рефакторинге:
— контроллер принимает FormRequest, дёргает сервис, возвращает Resource;
— бизнес-логика живёт в сервисах, чтобы переиспользоваться в API, CLI и очередях;
— доступ к БД — через репозитории, без Booking::where() в сервисах;
— сразу версионируй API (/api/v1), иначе первый же breaking change принесёт боль.
По SaaS-специфике: Sanctum для SPA, Cashier + Stripe для подписок (писать биллинг руками — плохая идея), stancl/tenancy если нужна изоляция тенантов. Тяжёлое отправляем в очереди на Redis, дебаг с Telescope локально и Sentry в проде.
🔗 Читать оригинал
Библиотека пхпшника
#book_code
🔥8👍3❤2😁2👾1
Forwarded from Библиотека собеса по PHP | вопросы с собеседований
Баг не воспроизводится локально. На проде один юзер видит чужие данные 👇
📦 Задание
Стартап запустил SaaS. Первые две недели тишина. А потом в поддержку прилетело: «Я зашёл в кабинет и увидел чужой аккаунт». В коде сессий и авторизации не трогали давно, там всё стабильно.
// src/Auth/UserSession.php
class UserSession
{
private static ?User $currentUser = null;
public static function set(User $user): void
{
self::$currentUser = $user;
}
public static function get(): ?User
{
return self::$currentUser;
}
public static function clear(): void
{
self::$currentUser = null;
}
}
// src/Middleware/AuthMiddleware.php
class AuthMiddleware
{
public function __construct(
private readonly UserRepository $userRepository,
private readonly JWTService $jwt,
) {}
public function handle(Request $request, callable $next): Response
{
$token = $request->headers->get('Authorization');
if (!$token) {
return new Response(status: 401);
}
$payload = $this->jwt->decode(str_replace('Bearer ', '', $token));
$user = $this->userRepository->find($payload['sub']);
UserSession::set($user);
return $next($request);
}
}
// src/Controller/DashboardController.php
class DashboardController
{
public function index(): Response
{
$user = UserSession::get();
return new Response(
body: $this->renderDashboard($user),
);
}
}
// src/Console/CacheWarmupCommand.php
class CacheWarmupCommand
{
public function execute(): void
{
$users = $this->userRepository->findAll();
foreach ($users as $user) {
UserSession::set($user);
$this->warmupForUser($user);
}
// прогрев завершён
}
}
🔹 Задачи
— Объяснить механизм утечки
— Объяснить, как CacheWarmupCommand триггерит баг и при каком race window
— Переписать UserSession так, чтобы устранить проблему архитектурно, а не патчем
Ставьте → 🔥 если нравится формат. Если нет → 🌚
💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍3❤2🤔1
🏭 PHP дотянулся до промышленных PLC
Исторически связать PHP с OPC UA (протокол SCADA, контроллеров Siemens, датчиков Industry 4.0) означало городить шлюз на Python/Node, тащить C-расширения через FFI или звать внешние процессы из шелла. Костыли, латенси, второй стек в проде.
Проект php-opcua закрывает этот разрыв — нативная реализация бинарного протокола OPC UA на чистом PHP. Ноль C-зависимостей кроме ext-openssl.
Что в экосистеме:
— opcua-client: транспорт, крипто (10 security policies, включая ECC), сессии, подписки;
— opcua-session-manager: ReactPHP-демон держит сессию живой, хендшейк 150мс → 5мс на запрос;
— laravel-opcua и symfony-opcua: фасады, DI, события, кеш через Redis/PSR-16;
— opcua-client-nodeset: 807 сгенерённых DTO из 51 companion spec (Robotics, CNC, ISA-95).
Три строки и Eloquent-модель читает температуру с цеха. Без сайдкаров.
🔗 Подробнее
Библиотека пхпшника
Исторически связать PHP с OPC UA (протокол SCADA, контроллеров Siemens, датчиков Industry 4.0) означало городить шлюз на Python/Node, тащить C-расширения через FFI или звать внешние процессы из шелла. Костыли, латенси, второй стек в проде.
Проект php-opcua закрывает этот разрыв — нативная реализация бинарного протокола OPC UA на чистом PHP. Ноль C-зависимостей кроме ext-openssl.
Что в экосистеме:
— opcua-client: транспорт, крипто (10 security policies, включая ECC), сессии, подписки;
— opcua-session-manager: ReactPHP-демон держит сессию живой, хендшейк 150мс → 5мс на запрос;
— laravel-opcua и symfony-opcua: фасады, DI, события, кеш через Redis/PSR-16;
— opcua-client-nodeset: 807 сгенерённых DTO из 51 companion spec (Robotics, CNC, ISA-95).
$client = ClientBuilder::create()
->connect('opc.tcp://192.168.1.100:4840');
$temp = $client->read('ns=2;s=Temperature');
echo $temp->getValue(); // 23.5
Три строки и Eloquent-модель читает температуру с цеха. Без сайдкаров.
🔗 Подробнее
Библиотека пхпшника
👍12🔥6💯1
Forwarded from Библиотека задач по PHP | тесты, код, задания
Что будет результатом кода?
Anonymous Quiz
24%
Применяем скидку!
23%
Скидка недоступна. Итого: 29
49%
Скидка недоступна. Итого: 28.999999999999
3%
fatal error
😁8🤔4👍3🔥2
🦾 Почему ваши AI-продукты на базе LLM ломаются (и как это чинить)?
Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».
🗓️ Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы
🧑🏻💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.
🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.
👉 Занять место на вебинаре
Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».
🗓️ Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы
🧑🏻💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.
🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.
👉 Занять место на вебинаре
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🥱3🔥1😁1🤔1
⌨️ Топ-вакансий по PHP за неделю
PHP/Vue.js Developer — офис (Москва) — TRIBE
PHP и Node.js разработчик — от 1000 до 2500 $ — удалёнка
РНР-разработчик (Авто.ру Бизнес) — 200 000 — 300 000 ₽ — гибрид (Москва, Санкт-Петербург )
➡️ Еще больше топовых вакансий — в нашем канале PHP Jobs
PHP/Vue.js Developer — офис (Москва) — TRIBE
PHP и Node.js разработчик — от 1000 до 2500 $ — удалёнка
РНР-разработчик (Авто.ру Бизнес) — 200 000 — 300 000 ₽ — гибрид (Москва, Санкт-Петербург )
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1
🎤 Ваши знания по ИИ-агентам + наша аудитория в 1 млн человек = профит
Мы в Proglib активно качаем тему ИИ-агентов. Если вы в теме, то у нас есть предложение 👇
Что с нас?
- Огромный охват: пропиарим ваши соцсети и продукты на 1 000 000+ айтишников.
- Личный бренд: станете узнаваемым экспертом в самой горячей нише 2026 года.
- Никакой рутины: наши редакторы сами упакуют ваши мысли в крутые посты.
Что с вас?
Любой экспертный контент по ИИ-агентам: кейсы из прода, шпаргалки, статьи, наработки по стеку (LangGraph, CrewAI, AutoGen и др.) или просто мысли по архитектуре.
👉 Стать экспертом и заявить о себе
Мы в Proglib активно качаем тему ИИ-агентов. Если вы в теме, то у нас есть предложение 👇
Что с нас?
- Огромный охват: пропиарим ваши соцсети и продукты на 1 000 000+ айтишников.
- Личный бренд: станете узнаваемым экспертом в самой горячей нише 2026 года.
- Никакой рутины: наши редакторы сами упакуют ваши мысли в крутые посты.
Что с вас?
Любой экспертный контент по ИИ-агентам: кейсы из прода, шпаргалки, статьи, наработки по стеку (LangGraph, CrewAI, AutoGen и др.) или просто мысли по архитектуре.
👉 Стать экспертом и заявить о себе
Один из недооценённых скиллов в разработке — это умение провести правильную границу между сервисами. Большинство «распределённых монолитов», которые мы встречаем в продакшене, рождаются именно здесь.
Разобрали тему — держи шпаргалку 👇
🔹 Что такое Service Boundary
Это контракт на три вещи:
— за что сервис отвечает;
— какими данными он владеет;
— как он общается с соседями.
Пример: в компании HR не лезет в финансы, а склад не занимается зарплатами. Так же должны работать твои сервисы.
🔹 4 принципа, которые работают
1. Business Capability First
Режь по бизнес-функциям, а не по техническим слоям. OrderService, PaymentService, UserService — потому что именно так бизнес думает о системе.
2. Single Responsibility
Один сервис — одна ответственность. Если в описании сервиса есть союз «и», это уже тревожный звоночек 🚨
3. Data Ownership
У каждого сервиса своя БД. Без исключений. Shared database = shared pain.
4. Loose Coupling
Только API, никакого прямого доступа к чужим таблицам.
🔹 Классический антипаттерн
// ❌ Бизнес-логика смешана в одном сервисе
class BadOrderService
{
public function processOrderAndPayment(int $id): string
{
$order = $this->orderRepository->findById($id)
?? 'Order not found';
$paymentStatus = 'Payment Successful'; // 🚨 чужая ответственность
return $order . ' | ' . $paymentStatus;
}
}
Выглядит безобидно, пока не нужно масштабировать платежи отдельно, сменить платёжного провайдера или добавить retry-логику только для оплаты. Тут и начнутся реальные проблемы.
🔹 Советы из практики
→ Начни с монолита. Не дроби систему заранее. Сначала пойми домен, потом режь по швам.
→ Следи за chatty communication. Если сервис делает 10 вызовов к соседям на каждый запрос — граница явно проведена не там.
→ Изучи DDD. Bounded Context из Domain-Driven Design — лучший инструмент для поиска правильных границ. Инвестиция окупается быстро.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2
При создании опций для CLI-приложений можно улучшить UX, реализовав автозаполнение для пользователя. Это можно сделать с помощью метода anticipate, предоставляемого Laravel 🚀
Библиотека пхпшника
#vardump
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2❤1