Сервісний шар і контролери в yii2

Продовжуємо занурення в проектування і розробку. У минулій статті про проектування доменних сутностей ми склали повноцінну сутність-агрегат предметної області Employee зі своєю власною бізнес-логікою для опису об'єктів співробітників. Тепер потрібно якось працювати з нею з контролера, зберігати в базу даних і діставати назад. Але наш Employee не містить жодного рядка по роботі з базою даних, тому сам зберігатися не вміє. Що ж з цим робити?

Зазвичай в цьому випадку створюють зовнішній об'єкт сховища (Repository), який буде керувати збереженням сутностей приблизно такими методами:

І саме цей EmployeeRepository зможемо використовувати в потрібних нам місцях. Наприклад, в сервісах додатки. Що це за сервіси і що вони роблять?

сервіс додатки

Весь код записувати в контролері незручно. Причини цього озвучимо нижче. Замість цього є популярна у вузьких колах практика код з логікою з контролера витягувати в окремі класи, звані сервісами рівня додатки (або прикладні сервіси) і вже з контролерів звертатися до них. Особливо не будемо вдаватися в термінологію, а просто подивимося, що це таке.

У нашому випадку для взаємодії з контролерами браузера або API нам знадобиться якийсь прикладної сервіс EmployeeService. який використовуючи сховище EmployeeRepository і диспетчер подій EventDispatcher здійснював би всі операції з сутністю співробітника:

Ми можемо передавати велику кількість параметрів кожному методу:

Але, щоб не плутатися в їх порядку і кількості, ці набори ми зібрали в окремі типізовані структури передачі даних (Data Transfer Object) на зразок AddressDto:

Після пізнання дзену на інтенсивний по ООП багато просили зробити що-небудь істотне на Yii2 Framework, щоб застосувати нові знання на практиці. І навіть на форумі згадали, що чекають від мене курс по фреймворку. Як і обіцяв в блозі, скоро запустимо великий багатоденний майстер-клас з розробки інтернет-магазину на Yii2 з використанням кращих практик.

Отже, продовжимо! Ми вже трохи навчилися проектувати суті в першій частині і навіть підготували невеликий прикладної сервіс у другій. І домовилися, що нам для зберігання доменних сутностей в базі потрібно зробити якийсь репозиторій. І навіть зробили його тестовий емулятор і підготували працюють тести. Перед вивченням будь-яких готових рішень сьогодні навелосіпедім власну реалізацію сховища без використання сторонніх ORM-систем.

Деякі розробники часом дивуються, як можна програмувати на фреймворками без використання CRUD і ActiveRecord, і чому таку «легку» на перший погляд пряму роботу з полями в базі даних недолюблюють тру-ООП-шники. Та й багато хто питає, що в тестах потрібно тестувати, а що не потрібно. І чи потрібно перевіряти приватні методи чи ні? Розберемося в цих питаннях.

Вебінар-скрінкасти про порівняння протоколів віддаленого виклику процедур RPC, доступу до об'єктів SOAP і архітектури REST для реалізації API для роботи з сайтом. Розглянули написання RESTFul API для проекту на Yii2 Framework і його тестування з використанням Codeception.

А що робити якщо на такі речі

Контролери смикають тільки EmployeeService. Він у нас і є набором юз-кейсів, які вже оперують сутностями. До сутностей безпосередньо контролер ніякого доступу не має.

Дякуємо. Із задоволенням провів пів години свого життя за читанням вашої статті.
З нетерпінням чекаю продовження!

Так само пара питань:

1. У Dto об'єктах дозволяється робити поля публічними? Чи не є це порушенням інкапсуляції?

2. Якщо у сервісу є кілька методів, наприклад, описаний вами UserService. В якихось методах потрібен passwordHasher, в якихось немає. Виходить що для того, що викликати метод changeEmail нам все одно потрібно передати в конструктор об'єкт passwordHasher, який в даному випадку нам не потрібен. Що зазвичай робиться в таких випадках? Сервіс розділяється на кілька частин? Або використовується як є?

1. DTO - це структура даних (заміна асоціативного масиву), а не повноцінний об'єкт.

2. У підході CQRS з Command Bus на кожну команду (DTO) пишеться по одному класу-обробника. Наприклад, для команди ProductCreateCommand робиться ProductCreateHandler і з контролера все команди закидають в шину як тут і тут. Виходить більше коду, але зайві об'єкти не іньектятся. Тому або розділяють на декілька частин, або не турбуються за зайві полкілобайта пам'яті для PasswordHasher.

А куди можна додати транзакцію? Може в контролері що щось подібне?