Pětivrstvý model#
Základ celého systému. Bez jeho pochopení nedává zbytek dokumentace smysl — a hlavně se v kódu nedá vyznat, protože tatáž věc má v každé vrstvě jiné jméno a jinou roli.
Model#
Presenter tenký; jen volá manager
↓
Manager veřejné rozhraní domény, lifecycle hooky
↓
Service tenká vrstva, drží počty, deleguje
↓
Mapper převádí surová data na entity
↓
Repository JEDINÉ místo, kde je SQL
| Vrstva | Smí | Nesmí |
|---|---|---|
| Presenter | volat manager, plnit šablonu | dotazovat se do databáze, obsahovat doménovou logiku |
| Manager | doménová logika, hooky, orchestrace; injektovat jiné managery | psát SQL |
| Service | delegovat, držet totalCount |
doménová rozhodnutí, injektovat jiné service |
| Mapper | hydratovat data na entity | psát SQL |
| Repository | SQL a QueryBuilder | doménová logika |
Zkratka přes vrstvu se vždycky vrátí
Dotaz z presenteru přímo do repository funguje — dokud někdo nepotřebuje totéž volat z jiné sekce. Pak se logika duplikuje a jedna z kopií se přestane opravovat.
Manager je JEDINÁ vrstva, která smí injektovat jiné managery
Service, Mapper i Repository jsou uzavřené: service drží jen svůj mapper, mapper
jen svou repository. XxxRunner nebo XxxResolver v Models/Services/, který si
injektuje manager, je porušení — patří do manager-tier vedle entitního manageru.
Front nesahá do databáze#
Nejdůležitější důsledek modelu. Front ani administrace nemají k databázi přístup.
Data si vyžádají od Api sekce přes most (app/Core/Bridge/ApiBridge.php).
Front Manager → Service → Mapper → Repository → ApiBridge
↓
interní volání (bez HTTP) nebo HTTP požadavek
↓
Api presenter
Tentýž modul má víc sad entit
App\UI\Front\Eshop\Models\Entities\Product a
App\UI\Api\Eshop\Models\Entities\Product jsou dvě různé třídy. Api entita je
Doctrine entita nad tabulkou, Front entita je výsledek hydratace odpovědi. Záměna
těch dvou je nejčastější chyba nováčka v tomhle systému — a projeví se jako
„TypeError někde úplně jinde“.
Mezi sekcemi teče POLE, ne objekt
Přes most jde array. Objekt na druhé straně teprve vzniká hydratací — proto jsou
v systému dva hydrátory, každý pro jednu stranu.
Front manager umí jen číst#
Základní front manager má jen čtecí traity — GetTrait, GetByTrait,
GetAllTrait, TotalCountTrait. Zápis si musí manager vyžádat explicitně:
use App\Core\Traits\Shared\Models\Managers\DeleteTrait;
use App\Core\Traits\Shared\Models\Managers\SaveTrait;
class AdvertFollowerManager extends BaseManager
{
use SaveTrait;
use DeleteTrait;
}
Dělá to 25 ze 111 frontových managerů. Admin a Api základ má zápis rovnou v sobě.
Je to záměrná brzda, ne opomenutí
Zápis z veřejného webu je výjimka, která má být vidět v kódu — ne něco, co se „prostě dá“.
Bázové třídy#
Každá vrstva má předka v app/Core/Base/, a to zvlášť pro každou sekci:
app/Core/Base/
Api/ Front/ Admin/ Cron/ Script/ Shared/
Models/Managers/Manager.php
Models/Services/…
Models/Mappers/…
Models/Repositories/…
Sdílené implementace jsou v app/Core/Traits/<Sekce>/Models/.
Mapper má dvě podoby#
| Kde | Složka | Co dělá |
|---|---|---|
| Api modul | Mappers/Db/ |
mapuje nad databází |
| Front/Admin modul | Mappers/Api/ |
hydratuje odpověď z API |
Je to tatáž vrstva ve dvou různých rolích — proto se jmenuje stejně.
Postup: přidávám čtení nové entity#
- Api strana: entita → repository (SQL) → mapper → service → manager →
get*Data()v presenteru s guardem oprávnění. - Klíč oprávnění do databáze ve tvaru
:Api:<Modul>:<Entita>:<akce>. - Front/Admin strana: varianta entity s hydratačními atributy → repository
s
apiEndpoint→ mapper → service → manager. - Presenter injektuje jen manager a volá
findAll(…)->execute(). - Ověř obě cesty — přes stránku i přímo přes most.
Podrobně Volání API a Konvence kódu.
Proč to tak je#
Admin, Front i cron jdou stejnou Api cestou. Doménová logika v manageru tedy platí pro všechny vstupy naráz — nemusí se psát třikrát a nemůže se rozejít.
Vedlejší efekt: API je hotové jako první
Protože Front sám používá Api, je API pro třetí strany funkční od začátku, ne až jako dodatek. Viz manuál pro API.
Cena je jedno hrdlo navíc
Každé čtení jde přes get*Data(), tedy i přes autorizační guard a přes
clearDoctrine(). To druhé je nejzávažnější landmina systému.
Navazující kapitoly: Struktura projektu · Volání API · Hydrátory · Identity mapa · Konvence kódu