Tabela de legatura intre entitati

M-am impotmolit in legatura a doua entitati:

View - entitate1
User - entitate2

am creat tabela ViewUser care contine un ViewId, UserId si Role

in tabela View, am o coloana UsersCount care de fapt e un select count(*) from ViewUsers where viewId = ?

de ce am nevoie de acel UsersCount in tabela Views? Pentru ca in pagina web, in lista de views vreau sa afisez coloana UsersCount si sa pot face sortare dupa ea. Daca as fi luat campul asta calculat din ViewUsers, ar fi trebuit ca atunci cand userul da click pe coloana respectiva din headerul tabelului, ar trebui ca sortarea sa fie calculata dintr-o tabela externa (celei Views) si m-as complica.

Astfel, cand voi adauga o linie noua in ViewUser, voi actualiza si coloana UsersCount din tabela Views.

Problema: dependinta circulara in servicii:

  • pentru ca am ales sa pastrez View si ViewUser ca entitati diferite, cand adaug o linie noua in ViewUser voi avea nevoie sa actualizez si View. Aveam deja injectat ViewUsersService in ViewsService pentru ca atunci cand sterg un view, am nevoie sa-i sterg toate dependintele. Astfel, in ViewUsersService.add() trebuie sa fac un get din ViewsService.getById() si sa-l actualizez. Aici apare problema de dependinta circulara.

rezolvari?:

  • daca era frontend, as fi scos codul intr-un serviciu comun si evitam asta.
  • integrez ViewUser in View (cum ar fi trebuit de la inceput, dar am incercat ca toate tabelele de legatura sa fie reprezentate prin entitati in api, pentru a nu aglomera clasele principale foarte mult); si pot pastra repourile diferite ViewsRepo si ViewUsersRepo (le injectez pe ambele in ViewsService)
  • elimin campul UsersCount din tabela Views si pentru fiecare rand din getViewsList() calculez UsersCount, doar ca sunt nevoit sa renunt la sortarea din frontend (ori sa creez functie speciala care sa sorteze pe agregate = necesita timp)

multam :smiley:

mai sunt dator cu un raspuns pe alt topic, incerc sa-mi fac putin timp :cowboy_hat_face:

Poti spune si limbajul si frameworkul?

ma gandeam ca nu conteaza :cold_face:

Scala, Play framework

atat cat am prins din articole si de la fostul loc de munca, incerc sa respect DDD

un raspuns scurt gasit pe stackoverflow:

Pragmatically spoken (I’m not a DDD expert), I would say that a link table shouldn’t pop up in the domain unless it is an entity on it’s own (i.e. you need to attach behaviour to it, or it has properties other than the foreign keys).

inteleg ca o tabela de legatura nu prea poate fi o entitate, insa chiar si pentru astea scriu CRUDuri, astfel ca n-as vrea ca intr-un controller sa am CRUD pentru mai multe clase, ar arata ceva de genul:

getFiltered (view)
getById (view)
add (view)
update (view)
delete (view)
getUsers (viewUser)
addViewUser (viewUser)
deleteViewUser (viewUser)
updateViewUserRole (viewUser)

cu toate ca le pot delimita usor prin nume, as avea controllerul si serviciul cu vreo 15 metode (imi par multe, mai am si cateva functii private care ajuta local).

Repourile sunt alta poveste, pe acestea le pot sparge pana la cel mai mic nivel (ViewRepository il am spart in 3) pentru ca le folosesc doar in servicii (si evit dependintele circulare)

In C# era o chestie misto, puteai structura codul pe regiuni

#region View
...
#endregion

#region ViewUser
...
#endregion

ar fi ajutat in clasele cu prea multe metode.

ar fi trebuit sa fie ceva simplu (e o tabela de legatura a naibi), dar din cauza ca se pot updata una pe alta, am dat de problema asta. In mod normal, ViewUser ar fi trebuit sa fie child pentru View si atat. Dar pentru ca nu vreau sa “scada” performanta atunci cand fac get pe lista de views pentru ca am nevoie de join cu viewUsers pentru a calcula countul, prefer sa-l salvez intr-o coloana si sa-i fac update de fiecare data cand ViewUser se modifica.

Ca sa nu ai probleme de constiinta: o tabela de legatura care nu contine alte informatii NU va fi entitate. In cazul tau ai nevoie de informatii suplimentare, deci e corect sa o faci entitate.

Cat despre problema in sine, eu as arunca un eveniment UserCreate la adaugarea unui User si as face un ViewSubscriber care prinde evenimentul si incrementeaza coloana UserCount.

Nota: nu am lucrat in Scala.

da, asta am facut pana la urma :rofl: desi nu este cel mai corect, din ViewUsersService fac un get pentru view in ViewsRepository si mai tarziu update tot de aici (pana acum am incercat pe cat posibil sa nu import intr-un serviciu, repositoryul altei entitati, dar nu pot evita asta aici, fara un al 3lea serviciu comun)

Cred ca era cel mai corect asa, dar am nevoie de ceva mai mult timp pentru a face asta (documentatie) si acum nu-mi permit. Daca inteleg corect ar trebui sa am un event bus la care asculta ambele servicii, pattern folosit pentru microservicii.

Salut,

Eu nu as include count-ul la nivel de tabel. Poate stergi, poate adaugi, o sa trebuiasca tot timpul sa ai grija de el.

Initial, ori faci un view daca iti suporta baza de date (ad-literam un view) ca e un select … group by; iar apoi la nivel de orm poti pur si simplu sa faci join pe acel view.

Daca nu se vrea mergerea pe view-uri, nici un query ad-hoc nu e de speriat; pana la urma e pur si simplu un count, iar daca ai indecsii setati ok nici nu-l vei simti.

M-as mira sa nu mearga metodele de mai sus chiar si pentru seturi de date mari. Dar, daca seturile cresc si observi ca acolo se pierde timp pe query, atunci de-abia m-as gandi sa tii acel count in timp real (incrementand si scazand) prin evenimente.

1 Like