Адаптер підганяє один чужий обʼєкт під інтерфейс, якого вже чекає ваш код, і поведінки не змінює. Фасад вводить новий, простіший вхід до кількох сервісів підсистеми й сам знає порядок їх виклику. Фасади Laravel (`Cache::`, `Mail::`) - це статичні проксі до одного сервісу з контейнера, підсистеми за ними немає, тож з GoF Facade їх повʼязує лише назва.
Архітектура для Middle: питання на співбесіду
12 питань рівня Middle з теми Архітектура з розгорнутими відповідями, порадами та перевіркою.
Структура в них однакова: контекст тримає обʼєкт за інтерфейсом і делегує йому роботу. Різниця в тому, хто і коли цей обʼєкт міняє: стратегію обирає клієнт або конфіг, і вона нічого не знає про інші, а стан обʼєкт перемикає сам після дії, і кожен стан знає, куди з нього можна перейти.
Репозиторій виправданий, коли він виглядає як колекція агрегатів у мові домену й тримає доменний шар без Illuminate чи Doctrine; він шкодить, коли перетворюється на generic CRUD-обгортку, що повертає той самий Builder або модель і лише додає шар.
На практиці найчастіше працюють два принципи: єдина відповідальність тримає класи малими й зрозумілими, інверсія залежностей робить код тестованим; решта є наслідками, і сліпе дотримання всіх пʼяти породжує зайві абстракції.
Value object — незмінний обʼєкт без ідентичності, який описується лише своїм значенням: він валідує себе в конструкторі, тому в системі не існує невалідного екземпляра, і порівнюється за вмістом, а не за посиланням.
Доменна подія - це зафіксований факт у минулому часі, названий мовою бізнесу (`OrderPaid`), який сутність записує в себе, а застосунковий шар публікує після коміту. Події фреймворку (`kernel.request`, `eloquent.updated`, `postPersist`) описують технічний життєвий цикл запиту чи рядка, а інтеграційна подія - це окремий публічний контракт для інших сервісів, з власною версійованою схемою, який виїжджає через outbox і доставляється at-least-once.
Робочий набір у PHP невеликий: Strategy за інтерфейсом із вибором у фабриці, Decorator для наскрізних дрібниць типу кешу й логів, Observer для реакцій на зміну стану. Фреймворки вже зібрані з них (драйвери кешу і сесій, `#[AsDecorator]`, Eloquent-обсервери), а Singleton у PHP-застосунку реалізує контейнер через `singleton()` чи shared-сервіс, не сам клас.
Обидва патерни передають запит послідовністю обробників, але з різним наміром: у Chain of Responsibility запит обробляє перша ланка, яка за нього взялася, і далі він не йде, а в Pipeline працює кожен крок і передає свій результат наступному. Middleware в Laravel і Symfony Messenger поєднує обидва: зазвичай шар передає запит далі й може змінити відповідь на зворотному шляху, але будь-який шар може зупинити обробку, повернувши відповідь сам.
Factory Method відповідає на питання «який клас створити»: базовий код просить продукт у власного методу, а конкретний клас визначає підклас, як `createRedisDriver()` у нащадках `Illuminate\Support\Manager`. Builder відповідає на питання «як зібрати один складний обʼєкт»: кроки з назвами й перевірка всіх правил разом у `build()`. Model factories у Laravel з Factory Method мають спільну лише назву: за змістом це незмінний будівельник тестових даних.
Стиль API вибирається за клієнтами і за тим, що ви готові втратити: REST дає безкоштовне HTTP-кешування й прозорі логи, але ліпить over-fetching і зайві раунд-трипи; GraphQL забирає вибірку полів у клієнта, натомість вимикає кеш на CDN і переносить N+1 та авторизацію в резолвери; RPC виграє у внутрішній комунікації зі строгим контрактом. Для одного власного фронтенду GraphQL зазвичай зайвий - дешевше sparse fieldsets, include і BFF-ендпоінт під екран.
CQRS — це розділення операцій зміни стану (команди) і операцій читання (запити) на різні моделі: команди йдуть через доменні агрегати з інваріантами, читання — окремими DTO або навіть сирим SQL. Event sourcing і окрема база для читання — необовʼязкові додатки, а не частина визначення.
Порт - це інтерфейс, який оголошує саме ядро застосунку під свою потребу, адаптер - реалізація цього інтерфейсу в інфраструктурі поверх конкретної технології. Домен і Application залежать лише від портів, зв'язування адаптерів робить service provider у Laravel або services.yaml у Symfony, а в тестах ті самі порти закриваються in-memory адаптерами.