ABONAMENTE VIDEO REDACȚIA
RO
EN
×
▼ LISTĂ EDIȚII ▼
Numărul 8
Abonament PDF

Probleme arhitecturale în proiecte Liferay

Alexandra Coldea
Java Developer
@ISDC
PROGRAMARE


Într-un mediu de afaceri din ce în ce mai agile, cu tot mai multe companii care concurează pentru aceeaşi cotă de piaţă, posibilitatea de a dezvolta aplicaţii cu multe funcţionalităţi "out of the box", Liferay este un framework cel puţin interesant.

Acest articol analizează probleme arhitecturale care trebuie adresate la începutul proiectului pentru a obţine un produs flexibil fără ajustări majore ulterioare.

Cu Liferay sau fără Liferay

Liferay este un portal open source sub licență GPL, pentru aplicații enterprise. Vine împreună cu multe funcționalități personalizabile: user management, multi-tennant și multi-language, CMS, integrare cu motoare de reguli ("rule engine"), cu motoare de căutare și multe altele "out of the box".Funcționeză pe o serie de application servers (printre care JBoss, GlassFish), servlet containers (Tomcat, Jetty, Resin) și cu multe DBMS-uri. Poate fi deployat în cloud.

Înainte de începerea oricărui proiect trebuie pusă întrebarea: este într-adevăr nevoie de Liferay? Astfel, dacă e nevoie de un portal complex, o aplicaţie care administrează conţinut mult atunci se potriveşte. Dacă aplicaţia este mare ca dimensiune, dar nu e orientată spre conţinut ("content-centric"), Liferay poate să fie util, dar nu e recomandabil. În schimb, dacă aplicaţia este simplă şi manipulează conţinut puţin, atunci Liferay adaugă prea multă complexitate. Trebuie să se ţină cont şi de faptul că Liferay este un container de portleţi şi nu merită să se folosească pentru aplicaţii web clasice.

Service Builder sau Layer de servicii propriu

Service Builder-ul este un tool dezvoltat de Liferay pentru a automatiza crearea interfeţelor şi claselor folosite de portal și portleţi pentru layer-ele de servicii și acces la date. Se generează codul pentru bean-uri, Persistenţa, Model şi mapările de Spring. Acesta este tool-ul folosit şi de developeri Liferay pentru a genera serviciile din Liferay core. De aceea, a fost îndelung testat şi problemele rezolvate sau cel puţin identificate. Problemele găsite sunt logate în Jira și sunt publice: http://issues.liferay.com/browse/LPS. Service Builder-ul asigură tranzacționalitatea serviciilor, are un mecanism de clustering și chiar oferă posibilitatea de a internaționaliza orice entitate cu efort minim.

Totuşi, din punctul de vedere al documentaţiei, Service Builderul beneficiază doar de o pagină de wiki care nu este foarte detaliată. Aceasta înseamnă că, dacă nu întelegi exact ce fac anumite atribute, trebuie să investighezi codul generat pentru a anticipa cum se va comporta. De obicei, nevoia pentru aceste investigații nu poate fi identificată decât în timpul implementării și aceasta înseamnă întârzieri neprevăzute. În schimb, există training-uri organizate de Liferay unde se pot acumula cunoştinţele necesare.

Pe de altă parte, se poate opta pentru dezvoltarea unui layer de servicii propriu. Avantajul este deţinerea controlului complet asupra codului scris şi posibilitatea definirii documentaţiei proprii. Însă, overhead-ul de a defini două layere - persistență şi servicii - cu interfeţele şi nivelul de abstractizare oferit de Service Builder este foarte mare. Astfel, timpul de implementare și livrare ("time to market") va fi foarte ridicat.

Totuşi, există unele cazuri în care Service Builder-ul nu este necesar. De exemplu dacă se comunică cu un sistem 3rd party care administrează datele și expune servicii, va fi nevoie doar de apelarea lor.

În continuare vom discuta despre proiecte în care se folosește Service Builder-ul.

Decuplarea componentelor aplicaţiei

Ca în orice arhitectură pe layere, componentele trebuie menținute separate, dar decizia asupra nivelului de granularitate la care se separă în artefacte trebuie cântărită cu atenţie pentru că poate aduce o penalizare de performanţă sau poate face codul greu de înteles și menținut.

Figura 1 prezintă împarţirea logică a componentelor dintr-un proiect de Liferay. În mod implicit, Service Builder-ul generează api-ul separat de implementare, într-un folder din WEBINF. Aceasta este modalitatea Ant de structurare a proiectului. Modalitatea Maven este de a-l genera într-un modul separat.

Cât despre împachetarea componentelor, există mai multe modalităti de a face acest lucru, în funcţie de necesităţile proiectului. Pentru cel mai simplu proiect, este suficient să se împacheteze api-ul într-un jar şi portleţii împreună cu implementarea într-un war. Avantajul acestei soluţii e că odată ce contractul e definit implementarea se poate schimba şi i se poate face hot deploy. Teoretic, se poate modifica logica de business şi serverul nu trebuie oprit pentru a avea soluţia în producţie. Dar, din păcate, în practică de foarte puţine ori se va întampla să lucrezi un sprint la servicii şi să nu modifici ceva din interfată. Hot deploy-ul este mai mult pentru hot fixes.

Pentru a pastra un "separation of concerns" şi a creşte nivelul de izolare, se poate ca implementarea serviciilor să constituie un modul separat şi să fie deployat ca un jar, aşa cum e prezentat în Figura 2. Aceasta ar însemna că, dacă la un moment dat clientul se hotărăşte că doreste să stocheze datele într-un mod diferit, trebuie doar înlocuit modulul de implementare. Dezavantajul este că deploy-ul durează mai mult, fapt care se simte mai ales în timpul development-ului.

Din acest punct de vedere, dacă proiectul este unul mare şi foarte agil, cu un client imprevizibil, cea mai bună soluţie ar fi să se separe implementarea serviciilor într-un modul diferit de la început, dar se renunță la posibilitatea de a deploya hot fixes pe servicii. Pentru development se poate folosi o configurare asemănătoare cu cea din Figura 3, în care acest modul e împachetat împreună cu portleţii într-un war şi astfel se poate face hot deploy şi nu se crează overhead pentru asta.

Un alt aspect care diferă de la proiect la proiect este modul în care portleţii sunt separaţi în plugin-uri. Variaţiile sunt foarte mari: în unul dintre proiectele pe care am lucrat, se folosea un plugin per portlet, pe când în altul exista un singur plug-in pentru toţi portleţii. Fiecare abordare are avantajele şi dezavantajele ei. În prima este foarte uşor să controlezi ce funcţionalităţi pui la dispozitie unui tenant, dar deployul durează mult şi comunicarea inter-pluginuri poate fi costisitoare. A doua este o variantă mai rapidă, tot business-ul fiind la un loc. Dar aceasta din urmă este avantajoasă numai până la un anumit număr de portleți.

Decizia depinde de necesităţile clientului și dimensiunea proiectului. Când aceasta din urmă începe să crească, funcţionalităţile care au o legătură logică sau au sens împreună pentru business, ar trebui să fie grupate în acelaşi plugin. Chiar dacă la începutul proiectului totul este dezvoltat împreună, este posibil ca la un moment dat clientul să vrea să pună la dispoziţie diverşilor tenanţi funcţionalităţi diferite.

Interacţiunea dintre componente

O problemă care de cele mai multe ori se pune prea târziu este standardizarea la nivel de proiect a comunicării dintre componente.

Liferay urmărește un Model Driven Architecture, unde Service Builderul acţionează ca un Model Driven Transformation Tool. Deci, layer-ul de servicii este cel responsabil cu transformarea entităților de back-end în entităţi care pot fi folosite de layer-ul de portleţi. Dar, la fel ca portleţii, serviciile pentru entităţile care au valoare de business împreună ar trebui grupate şi aceste grupuri să fie izolate unele de altele. Aceasta pentru că se poate decide la un moment dat că anumite funcţionalităţi trebuie mutate pe un complet alt environment. De exemplu, într-o aplicaţie care se ocupă cu managementul studenţilor, toate informaţiile referitoare la notele de la examene trebuie mutate într-un "trust center" din motive legale.

În aceste condiţii, pentru a face legătura dintre prezentare şi back-end, avem nevoie de servicii compuse, numite wrappere. Figura 4 prezintă această arhitectură. Comunicarea dintre nivelul de wrappere si nivelul portleţilor se face folosind DTO-uri sau business objects. Layer-ul de servicii expune entităţi de Service Builder şi toate operatiile pe care le face sunt atomice, pentru că totul e într-o tranzacţie. De aceea, nivelul de granularitate la care serviciile se împart în unităţi funcţionale trebuie cântărit cu grijă pentru a nu scoate din tranzacţii operaţii care ar trebui să fie atomice (ex. ștergerea unui student separată de ştergerea notelor lui). Mai mult, clientul poate să decidă că doreşte să îşi expună business-ul ca un serviciu (Software as a Service). Dacă proiectul e modularizat eficient, logica de business pentru o funcţionalitate e centralizată, această schimbare ar trebui să fie simplă, pentru că Service Builder-ul oferă posibilitatea de a expune servicii REST sau SOAP doar scriind o implementare în layer-ul de servicii. În schimb, dacă layerul cu wrapper-ul este cel care asigură consistenţa bazei de date, trecerea va fi una dureroasă.

Concluzii

În concluzie, nu există un mod standard de a structura o aplicaţie Liferay, există doar o serie de întrebări care trebuie puse la începutul fiecărui proiect. Cu cât aceste întrebări care standardizează modalităţile de lucru sunt puse mai târziu, cu atât refactorizarea pentru a avea un proiect consistent va fi mai costisitoare, flexibilitatea şi extensibilitatea vor fi afectate.

LANSAREA NUMĂRULUI 86

Prezentări articole și
Panel: Autonomous driving

Marți, 20 August, ora 18:00
Bosch R&D, Cluj-Napoca

Înregistrează-te

Facebook Meetup

Conferință

Sponsori

  • ntt data
  • 3PillarGlobal
  • Betfair
  • Telenav
  • Accenture
  • Siemens
  • Bosch
  • FlowTraders
  • MHP
  • Connatix
  • UIPatj
  • MetroSystems
  • Globant
  • Colors in projects