4. Arhitektura i dizajn sustava - Progi-Prijatelji/Repo-Prijatelji GitHub Wiki
Sustav je zasnovan na klijent-poslužitelj arhitektonskom stilu koji koristi višeslojnu arhitekturu. Ovaj pristup omogućuje jasno odvajanje odgovornosti, modularnost te lakše održavanje i nadogradnju sustava.
- Klijent-poslužitelj stil definira fizičku organizaciju sustava - klijenti, što su učenici i administratori, su odvojeni od poslužitelja koji obrađuje zahtjeve i pristupaju bazi podataka. Takva strukturiranost omogućuje jednostavnu interakciju i podjelu između frontenda i backenda.
- Višeslojna arhitektura očituje se u organizaciji unutar samog sustava kroz tri osnovna sloja: prezentacijski (React), poslovni (Node.js) i podatkovni (PostgreSQL baza). Time se osigurava visoka kohezija i niska povezanost.
Podsustav za autentifikaciju i korisničke račune: zadužen je za prijavu korisnika u aplikaciju putem korisničke adrese, promjenu lozinke i brisanje korisničkog računa.
Podsustav za upravljanje sadržajem: omogućuje administratorima pretragu vanjskih rječnika te dodavanje cijelog ili samo jednog njegovog dijela u sustav, kao i njihovo uređivanje i brisanje.
Podsustav za učenje i evaluaciju: omogućuje prevođenje riječi s materinjeg na strani jezik, implementira logiku premještanja riječi među posudama na temelju točnosti odgovora, različite modove koji provjeravaju ispravnost napisanih riječi i izgovora te ih ocjenjuju na ljestvici od 1 do 10.
Podsustav za sigurnost i autorizaciju: osigurava da je pristup podacima i funkcionalnostima aplikacije ograničen na temelju korisničkih uloga, implementira zaštitu osobnih podataka korisnika i sigurnosnu prijavu u skladu s OAuth 2.0. standardom.
Podsustav za infrastrukturu i održavanje: odgovoran je za praćenje performansi, skalabilnost sustava te osiguravanje trajnosti i oporavka podataka.
Projekt je preslikan na radnu (deploy) platformu koristeći Docker i Render, čime je osigurano stabilno okruženje za pokretanje aplikacije.
U Dockeru smo napravili image za frontend i banckend. Docker slika sadrži sve potrebne pakete i definira način pokretanja aplikacije. Render koristi tu sliku i pokreće je kao web servis bez dodatne ručne konfiguracije.
Deploy je izveden preko platforme Render, gdje su pokrenuta dva ključna servisa:
-
Web servis - pokreće Docker sliku koja obuhvaća i frontend i backend
-
PostgreSQL baza podataka - hostana unutar Render DB servisa
Konfiguracijske varijable (API ključevi, URL baze, OAuth postavke) definirane su unutar Render environment settings, čime se osigurava sigurna i jednostavna integracija svih dijelova sustava.
Ovime je aplikacija u potpunosti preslikana na radnu platformu kroz jasno definirane kontejnere, stabilan hosting i automatsko upravljanje infrastrukturom.
Za spremanje podataka sustava odabrana je relacijska baza podataka PostgreSQL čime se osigurava transakcijska pouzdanost i visok integritet podataka. U relacijskom modelu podaci su organizirani u tablice (entitete) koje su međusobno povezane vanjskim ključevima što omogućuje efikasno pretraživanje i provedbu kompleksne poslovne logike. Odnosi između entiteta (primjerice User, Word i Dictionarie) prikazani su putem dijagrama klasa izrađenog u alatu AstahUML koji je poslužio za vizualizaciju strukture podataka i njihovih veza.
U aplikaciji se koriste sljedeći mrežni protokoli:
-
HTTPS - omogućuje sigurnu i pouzdanu razmjenu podataka između prezentacijskog sloja (React) i logičkog sloja (Node.js).
-
TCP/IP - osigurava pouzdan prijenos paketa podataka između korisničkog računala i poslužitelja.
-
SSL/TLS - pruža enkripciju, osigurava zaštitu osobnih podataka i tokena tijekom prijenosa kroz mrežu.
-
OAuth 2.0 - koristi se za autentifikaciju i prijavu korisnika putem vanjskih servisa, kao što su Google, što omogućuje sigurno upravljanje pristupom bez pohrane lozinki u aplikaciji.
-
PostgreSQL protokol (SQL over TCP/IP) - koristi se za komunikaciju između logičkog sloja (Node.js) i podatkovnog sloja (PostgreSQL baze). Osigurava slanje SQL upita i primanje rezultata putem sigurne TCP veze.
Globalni upravljački tok predstavlja kružni tok podataka i informacija koji se pokreće interakcijom korisnika te se drži pravila višeslojne arhitekture - komunikacija samo sa susjednim slojem. Tok je centraliziran na poslužitelju gdje se odvija sva poslovna i sigurnosna logika.
Općeniti tok kroz aplikaciju izgleda ovako:
- Korisnik putem frontend sučelja izrađenog u Reactu pokreće određenu radnju (primjerice prijavljivanje u sustav) nakon čega se prema poslužitelju šalje HTTPS zahtjev s pripadajućim podacima.
- Pri prijavi kornsika, poslužitelj provodi autentikaciju i generira JWT koji sadrži korisnički identifikator. Nakon toga, svaki idući zahtjev se autorizira isključivo provjerom valjanosti i digitalnog potpisa tog JWT-a čime se ukida potreba za ručnom provjerom korisničkog ID-a nad bazom podataka.
- Ako je JWT valjan i korisnik ima potrebne ovlasti, zahtjev se obrađuje - dohvaćaju se podaci iz baze podataka pomoću SQL upita (preko TCP/IP), a rezultat se prosljeđuje nazad u Node.js.
- Nakon obrade, backend generira odgovor u JSON formatu s traženim podacima ili statusima radnje (primjerice "uspješna prijava" ili "pogrešan odgovor") i šalje ih Reactu gdje se zatim oni prikazuju krajnjem korisniku.
Ako zahtjev dolazi od korisnika s administratorskim ovlastima tada su omogućene dodatne funkcionalnosti poput dodavanja novih riječi. Ti se podaci također spremaju u bazu pomoću istog toka obrade.
Za pravilno funkcioniranje aplikacije potrebni su sljedeći sklopovsko-programski zahtjevi:
- minimalno 2GB RAM-a i dvojezgreni procesor
- stabilna internetska veza zbog komunikacije s poslužiteljem putem HTTPS zahtjeva
- moderni web-preglednik s isključenim naprednim sigurnosnim postavkama kolačića radi ispravnog rada autentifikacije
- za mod provjere izgovora riječi potreban je mikrofon i zvučnici na klijentu
- Node.js i React okruženje (verzija v.18 ili novija) i PostgreSQL baza podataka za koju je potrebno osigurati dovoljno prostora na disku (minimalno 1 GB)
Pri oblikovanju arhitekture sustava primijenjeni su sljedeći temeljni principi oblikovanja:
- visoka kohezija - svaki sloj ima jasno definiranu odgovornost pa se tako frontend bavi prikazom podataka, backend obradom poslovne logike, a baza pohranom.
- slaba povezanost - slojevi međusobno komuniciraju preko REST API sučelja čime se omogućuje paralelni razvoj i testiranje.
- hijerarhija i modularnost - omogućuje jednostavno dodavanje novih funkcionalnosti bez potrebe za izmjenom postojećih dijelova koda.
Moguće druge alternativne arhitekture:
-
Monolitna arhitektura: jednostavnija implementacija zbog toga što objedinjuje frontend i backend u jednu cjelinu no istovremeno otežava održavanje i fleksibilnost. Također, zbog načina funkcioniranja CDN-a (Content Delivery Network) odziv bi bio znatno sporiji jer se učitava više sadržaja. Zbog navedenik informacija, ova arhitektura je naposljetku odbačena.
-
Mikroservisi: podrazumijeva razdvajanje sustava na niz malih neovisnih servisa koji međusobno komuniciraju, što omogućuje visoku skalabilnost i neovisno razvijanje komponenti sustava. Takav pristup je optimalan za opširnije i brzorastuće sustave, što isključuje ovu arhitekturu upravo zbog svoje presloženosti za trenutni opseg projekta.
-
Klijent-poslužitelj: sustav se sastoji od klijentskog i poslužiteljskog dijela, izrađenog u React i Node.js, koji međusobno komuniciraju putem REST API sučelja. Frontend omogućuje interakciju korisnika s aplikacijom (unos podataka, slanje zahtjeva) dok backend te zahtjeve prima, obrađuje ih i komunicira s bazom podataka.
-
Baza podataka: relacijska baza podataka PostgreSQL služi za pohranu i dohvat podataka - čuva informacije o korisnicima i prijavama te riječima i rječnicima unutar tablica koje su međusobno povezane ključevima. Definirani entiteti su: User, Word, DictionaryWord, Phrase, Dictionary, Language, i UserLanguage. Korištenje ove baze podataka osigurava integritet i dosljednost podataka.
-
Datotečni podsustav: zadužen je za pohranu svih ostalih podataka koji nisu prikladni za direktnu pohranu u relacijskoj bazi podataka. Poslužitelj upravlja ovim datotekama te generira i vraća putanje klijentu za dohvat.
-
Grafičko sučelje: korisničko sučelje je izgrađeno korištenjem Reacta. Dizajnirano je tako da bude intuitivno i responzivno omogućujući korisniku jednostavnu navigaciju.
- Frontend slojevi:
- sloj za prikaz sadržaja korisniku - odgovoran je za vizualni prikaz podataka preko CCS-a i HTML-a.
- sloj za slanje zahtjeva poslužitelju - omogućuje komunikaciju između korisničkog sučelja i backenda na način da šalje HTTPS zahtjeve i prima odgovore.
- Backend slojevi:
- sloj za komunikaciju s bazom podataka - rukuje pohranom, dohvatom i ažuriranjem podataka u bazi.
- sloj za autentifikaciju korisnika - provjerava identitet korisnika i osigurava da ima odgovarajuće ovlasti (primjerice administrator može dodavati nove riječi dok običan korisnik, učenik, to ne može). Provjera ovlasti obavlja se čitanjem iz priloženog JWT tokena te se koristi OAuth 2.0.
- sloj za implementaciju logike aplikacije - središnji sloj backenda gdje se implementira sama logika i korišteni algoritmi, obrada podataka i sama koordinacija između baze i frontend zahtjeva.
Koristili smo relacijsku PostgreSQL bazu podataka.
U bazi podataka nalazi se entitet USERS koji sprema podatke registriranih korisnika u atributima userId, email, password i role. Entitet DICTIONARIES sadrži sve riječnike koje su admini napravili, a sadrže atribute dictId, dictName, description i langId koji je povezan s entitetom LANGUAGES i označava jezik rječnika. U LANGUAGES su zapisani svi jezici dostupni za učenje s atributima langId, langName i langImg. Entitet WORDS sadrži sve riječi, strane i prijevode, atributi su mu wordId, word, audioFile, langId te translationId kojim je entitet povezan na samog sebe tako da podatke strane riječi poveže s podacima prijevoda. DICTWORD je entitet koji povezuje riječi i rječnike jer svaka strana riječ može biti u jednom ili više rječnika pa su i atributi ovdje samo dictId i wordid. USERDICT je entitet koji ima atribute userId i dictId te se njime bilježi koje rječnike je učenik krenuo učiti, dok se entitetom USERWORD bilježi u kojoj posudi je koja riječ naučena po kojoj metodi učenja za svakog korisnika u atributima userId, wordId, method i container. Svaka riječ, strana i prijevod, može imati više fraza koje ju opisuju, a te fraze se zapisuju u entitet PHRASES s atributima phrase i wordId.
| Atribut | Tip podataka | Opis |
|---|---|---|
| userId | int | primarni ključ |
| charvar (100) | unique | |
| password | charvar (50) | |
| role | charvar (10) |
USERS opisuje svakog prijavljenog korisnika u aplikaciju. UserId se automatski određuje, email i password upisuje korisnik (password se sprema hashiran), a role definira ulogu korisnika te se automatski definira kao 'student' pri registraciji.
| Atribut | Tip podataka | Opis |
|---|---|---|
| langId | int | primarni ključ |
| langName | charvar (50) | |
| langImg | charvar (256) |
U LANGUAGES se spremaju svi dostupni jezici za učenje. LangId se automatski određuje pri stvaranju jezika, langName je naziv jezika, a langImg put do slikovne datoteke koja predstavlja taj jezik.
| Atribut | Tip podataka | Opis |
|---|---|---|
| dictId | int | primarni ključ |
| dictName | charvar (100) | |
| langId | int | strani ključ |
| description | charvar (256) |
U DICTIONARIES se spremaju svi rječnici. DictId se automatski određuje pri stvaranju rječnika, dictName je naziv pridružen rječniku, langId je strani ključ iz LANGUAGES koji označava na kojem jeziku je rječnik, a description je kratki opis sadržaja rječnika.
| Atribut | Tip podataka | Opis |
|---|---|---|
| wordId | int | primarni ključ |
| word | charvar (100) | |
| audioFile | charvar (256) | |
| langId | int | strani ključ |
| translationId | int | strani ključ |
WORDS je tablica koja sadrži sve strane riječi i hrvatske prijevode. WordId je identifikacija riječi te se automatski određuje pri stvaranju, word je riječ, audioFile je put do dokumenta u kojem je spremljen izgovor riječi, langId je strani ključ iz tablice LANGUAGES i označava jezik na kojem je riječ, a hrvatski prijevod strane riječi povezan je s translationId koji je zapravo wordId tog prijevoda (za hrvatske riječi, tj. kada je langId onaj od hrvatskog jezika, translationId je NULL).
| Atribut | Tip podataka | Opis |
|---|---|---|
| dictId | int | primarni ključ, strani ključ |
| wordId | int | primarni ključ, strani ključ |
DICTWORD povezuje tablice DICTIONARIES i WORDS, povezuje riječ sa svakim rječnikom u kojem se nalazi. Sastoji se od dictId koji je strani ključ iz DICTIONARIES i wordId koji je strani ključ iz WORDS.
| Atribut | Tip podataka | Opis |
|---|---|---|
| userId | int | primarni ključ, strani ključ |
| dictId | int | primarni ključ, strani ključ |
USERDICT povezuje tablice DICTIONARIES i USERS, povezuje korisnika sa svakim rječnikom kojeg je započeo učiti. Sastoji se od dictId koji je strani ključ iz DICTIONARIES i userId koji je strani ključ iz USERS.
| Atribut | Tip podataka | Opis |
|---|---|---|
| userId | int | primarni ključ, strani ključ |
| wordId | int | primarni ključ, strani ključ |
| container | int |
USERWORD povezuje tablice DICTIONARIES i WORDS, povezuje korisnika (userId) sa svakom riječi (wordId) koju je učio te u posudi (container) u kojoj se ta riječ za tog korisnika nalazi.
| Atribut | Tip podataka | Opis |
|---|---|---|
| phrase | charvar (256) | primarni ključ |
| wordId | int | strani ključ |
Fraze koje opisuju riječ spremljene su u tablicu PHRASES, phrase sadrži frazu, a wordId je strani ključ iz tablice WORDS za onu riječ koju fraza opisuje.
ER dijagram
Ovaj dijagram razreda prikazuje glavne entitete sustava i njihove međusobne odnose.
Atribiti klasa su privatni, zbog čega će svaka klasa (osim relacijskih) imati gettere i settere za te atribute.
Klasa User predstavlja korisnika koji se može registrirati, prijaviti, mijenjati lozinku i upravljati jezicima koje uči.
Klasa Dictionary sadrži riječi nekog jezika i posjeduje funkcije addWord i removeWord koje samo admin može pozvati. Također sadrži funkciju getStatus koja služi kao pomoćna funkcija koja provjera stanje riječnika (npr. može nam ispisati ukupni broj riječi, broj riječi bez prijevoda, broj riječi bez audioFile-a, itd.)
Klasa Word predstavlja riječ i njen prijevod, s povezanim frazama i izgovorom. Isto kao i za klasu Disctionary, sadrži funkcije koje samo admin može pokretati. Ima samoreferencijalnu vezu (translation: Word).
Klasa UserWord sadrži napredak učenja neke riječi za pojedinog User-a. Klasa, uz korisnika i riječi, sadrži metodu učenja i posudu u kojoj se riječ nalazi. Nakon što korisnik točno odgovori na pitanje, riječ se prebacuje na sljedeću posudu, tj. container se povećava za 1. Nakon što container pređe 5, korisnik je naučio riječ i bilježenje napretka za tu riječ se briše.