[pugMI] Dubbi su architettura / data mapper / repository ecc... HELP! :)
Simone Magnaschi
simone.magnaschi a gmail.com
Gio 16 Lug 2015 05:44:02 PDT
Grazie Gaetano,
l'approccio sembra molto interessante e sembrerebbe fare al caso mio dato
che parte dei dati li abbiamo su servizi esterni (quindi api e no db).
Ho provato a dare un'occhiata veloce a
https://github.com/ezsystems/ezpublish-kernel/ ma come immaginavo a una
code base abb. complessa.
Per caso hai articoli o risorse per poter approfondire quel tipo di
approccio?
Grazie ancora
S.
Il giorno 16 luglio 2015 11:51, Gaetano Giunta <giunta.gaetano a gmail.com>
ha scritto:
> Simone Magnaschi wrote:
>
> Ciao a tutti,
> volevo chiedervi un consiglio su come impostare una determinata sezione di
> un progetto su cui stiamo lavorando.
> Perdonatemi la lunghezza ma cerco di spiegarmi al meglio per inquadrarvi
> bene la situazione.
> Il cuore del problema è il Data Mapper Pattern e come gestire al meglio le
> classi che entrano in gioco.
>
> Premetto che non ho esperienze di DDD o particolari esperienze con ORM e
> similari quindi alcune cose che dirò potrebbero sembrarvi stupide o
> completamente senza senso.
>
> Nell'ottica di ristrutturare le classi che gestiscono gli "oggetti" su
> cui operiamo, ho iniziato a sviluppare un insieme di classi che si
> avvicinassero più o meno al data mapper pattern. Quindi ho le mie entità
> che sono POPOs con i loro metodi necessari per la logica delle entità
> stesse. Queste classi non hanno nessuna conoscenza del database (anche se
> hanno tra le proprietà gli id che a ben vedere con le entità c'entrano poco
> o niente).
> Ho poi costruito le mie classi repository che vanno a fare avanti e
> indietro tra db e entità (idratano gli oggetti e allo stesso tempo fanno la
> persistenza mappando le proprietà cn i campi del db).
>
> Non ho usato ORM come Doctrine (che tra l'altro non ho mai usato) o
> similari perchè l'applicazione che stiamo usando purtroppo è una legacy
> application in cui i dati sono sparsi tra due DB differenti (sql server +
> mysql). Ho preferito iniziare a costruire una semplice soluzione in casa
> piuttosto che sbattere la testa con una libreria che non sono sicuro che
> possa coprire il nostro caso.
> L'approccio che ho usato, in sè, ci ha portato dei vantaggi, soprattutto
> per quanto riguarda il salvataggio e la persistenza dei dati che ovviamente
> ora sono in un unica classe ecc ecc.
>
> I problemi che sto avendo sono di natura architetturale e ora cerco di
> entrare nel merito.
>
> Il progetto su cui stiamo lavorando è legato alla gestione di artisti
> musicali e dati correlati.
>
> Nel nostro caso abbiamo diversi livelli di dati differenti sparsi su N
> tabelle che rappresentano l'artista.
>
> PUNTO 1: DATI CHE DEFINISCONO L'ARTISTA
>
> a. dati di base (id, nome, tipo)
> b. dati estesi (nazionalità, data di nascita / morte, biografia,
> discografia ecc)
> c. aliases (nomi alternativi, nomignoli ecc)
> d. relazioni con altri entità di tipo artista (membri della band/ band
> con cui l'artista ha suonato ecc)
> e. relazioni con altre entità (genitori, fratelli ecc)
> f. meta dati vari (profilo deezer, sito ufficiale, fb, tw, g+ ecc)
> g. indice di copertura (identifica quanto l'artista è coperto dai nostri
> contenuti)
>
>
> PUNTO 2: ENTITA' ESTERNE CHE IN LINEA TEORICA SONO RELAZIONABILI
> ALL'ARTISTA
>
> - articoli / news che lo riguardano
> - recensioni
> - videoclip
> - foto
> - citazioni
> - ...
>
>
> Nel mio caso specifico l'entità artista può essere definita
> completamente con il punto 1. E' possibile costruire un artista istanziando
> un oggetto di tipo Artist.
>
> L'oggetto è un grafo in cui ho delle proprietà di base che coprono i
> punti a/b e ho delle collezioni di altri oggetti (ognuno definitio a parte)
> che gestiscono:
> - Alias punto c
> - Meta punto f
> - Relazioni punto d/e
> - Copertura e ranking punto g (..ehm..)
>
> Il tutto mi va bene, ho il typehinting, la logica sta nelle classi e ok.
>
> L'ArtistRepository è un oggetto piuttosto grande che manda avanti e
> indietro questi dati (complessivamente) tra tutte le tabelle coinvolte.
> Ha classici metodi pubblici da repository:
> - findById
> - persist (gestisce sia insert che update)
> - remove
> - ...
>
> Questo è ok ma ora mi si presentano diversi problemi che non so bene
> come risolvere. Questi i primi che mi vengono in mente:
>
> 1) PROBLEMI DI PERFORMANCE
>
> Se uso questo repository così com'è nell'applicazione ho dei problemi di
> performance (lasciando da parte il caching a cui arriverò dopo).
> Immaginiamo una scheda degli Oasis, se prendo gli oasis tramite repository
> e voglio rappresentare anche tutti i vari componenti, andrò a prendermi le
> relazioni, da qui creerò altri oggetti artista completi per poter
> semplicemente mostrare a video i loro nomi. Risultato, se per un artista mi
> ci vogliono 7-10 query nel DB, qui le moltiplico per tutti i componenti per
> nulla.
>
> Allora ho pensato a questa cosa: il repository potrebbe avere dei metodi
> -con fluent interface eventualmente- del tipo withBaseData / withMeta ecc
> che dicono al repository quali parti dell'oggetto Artista andare a
> recuperare.
> Quindi il mio $repo->fetchById() potrebbe diventare al caso
> $repo->withBaseData()->withRelations->fetchById() per andare a prendere un
> artista con i suoi dati di base ma anche le relazioni e nient' altro.
> Il problema è che se poi lanciassi un persist mi cancellerebbe tutti i
> dati che non ho recuperato xè per il repository quell'artista non ha alias
> metadata o altro.
> Quindi dovrei tenere traccia all'interno dell'entità artista di quali
> "parti" sono state idratate e quali no. In modo che all'atto del persist il
> repository può verificare cosa deve toccare e no.
>
> Questo mi genera una serie di perplessità:
> - dal punto di vista dell'entità il concetto di cosa è stato idratato o
> meno non ha assolutamente senso
> - l'aggiunta di metodi come withBaseData o withRelations rende il
> repository troppo specifico e difficilmente astraibile in un'interfaccia
> repository generica.
> - questo mi blocca nel caso volessi usare un decorator per aggiungere un
> livello di logging piuttosto che di caching tramite file / redis ecc
> - potrei certo costruire un'interfaccia ad hoc ma sto cercando di capire
> se può essere ripensato il tutto per salvare capra e cavoli
>
>
> Non ho una risposta precisa ma ti posso raccontare cosa e' stato fatto per
> eZPublish:
>
> v3/4:
> - tutti gli oggetti 'entity' sono dotati di proprerties con lazy loading.
> $artist->albums va a effettuare la query nel momento in cui si vi si accede
> - non ci sono repositories, pattern usata: activerecord
> - ogni volta che si modifica una property di una entity, questa si salva
> il suo stato dirty, che poi usa per la serializzazione
> - ogni entity va salvata singolarmente
> pros:
> - veloce e poca ram sprecata se si vuole caricare il dataset minimo di una
> entity
> - facile scrivere codice conciso, ma se si vuole l'autocompletion delle
> ide occcorre scrivere una gran massa di phpdoc
> cons:
> - poco uso di joins
> - pattern activerecord => brutta tendenza ad avere god classes
> - lazy loading => difficile avere il controllo sulle performances. Ci sono
> sviluppatori che p.e. accederanno ad un dato tramite query del tipo
> $artist->albums[0]->collaborators['singer']->albums[0]->picture, ovviamente
> in un loop...
>
> v5:
> - introdotti i repository services: 1 repo (magari vari repo nel tuo caso,
> visto che hai vari db) con grossomodo 1 service per ogni entity
> - no more lazy loading: tutto viene caricato esplicitamente a mano (in
> php): $artistService->fetchRelationsById($artistId)
> - ogni service e' quindi specifico, e ha in se la logica per persistere la
> entity in modo 'sparpagliato'
> - 'sotto' i repository services esistono gli storage services, che
> interagiscono coi vari db/nosql/filesystem
> - nelle chiamat REST, per evitare la chattines del protocollo, quando si
> carica una entity si puo' chiedere quale parte del dataset si vuole avere
> indietro, come proponi tu: pochi dati per compattezza, oppure tutti in una
> volta sola
> pros:
> - introdurre caching e logging e' stato fatto a livello degli storage
> services => facile!
> cons:
> - codice verboso => si scrive bene solo con una ide
> - architettura a lasagna con molti strati => impossibile debuggare senza
> una ide
> - architettura a lasagna con molti strati => non e' facile ottenere delle
> query ottimizzate
>
> I dati esterni sono trattati secondo lo stesso schema.
>
> Disclaimer: in eZP, il modello dati e' EAV, ovvero le classi entity non
> sono tipizzate.Esistono poche classi: 'contenuto', 'location', 'sezione'
> etc... non 'artista', 'pagina', 'album', 'track', 'casa discografica'.
> Questo fa si che i repository services non siano decine e decine.
>
> Ciao
> Gaetano
>
> 2) PROBLEMI DI RECUPERO DATI ESTERNI
>
> In questa situazione come è meglio recuperare i dati esterni? Come
> recupero ad esempio le foto o le news dell'artista? Devo renderli metodi
> dell'ArtistRepository e mapparli come relazioni nell'entità artista
> esattamente come faccio per gli altri dati? Così facendo non farei che
> peggiorare il punto sopra.
> Gli articoli o le foto ad esempio dovrebbero essere entità a sè con il
> loro repository ecc. Come faccio a legare i due?
>
> Perdonatemi la lunghezza, e grazie in anticipo :)
>
> Simone
>
>
>
> _______________________________________________
> Milano mailing listMilano a ml.grusp.orghttp://ml.grusp.org/listinfo.cgi/milano-grusp.org
>
>
>
> _______________________________________________
> Milano mailing list
> Milano a ml.grusp.org
> http://ml.grusp.org/listinfo.cgi/milano-grusp.org
>
>
-------------- parte successiva --------------
Un allegato HTML è stato rimosso...
URL: <http://ml.grusp.org/pipermail/milano-grusp.org/attachments/20150716/3cf483ae/attachment-0001.htm>
Maggiori informazioni sulla lista
Milano