classDiagram
ui ..> services
services ..> repositories
services ..> entities
repositories ..> entities
repositories ..> database
Koodi on jaettu niin, että ui sisältää käyttöliittymää, services sovelluslogiikkaa ja repositories pysyväistallennusta vastaavan koodin. Pakkaus database kuvastaa tiedostoja, jotka luovat yhteyden SQLite3-tietokantaan jota repositories käyttää komennoissaan. Pakkaus entities sisältää luokkia, jotka vastaavat tietokannassa olevia rivejä.
Käyttöliitymällä on seuraavat näkymät:
- Kirjautuminen
- Rekisteröityminen
- Hahmolista
- Yksittäisen hahmon tiedot
Käyttöliittymä on eristetty sovelluslogiikasta, ja sen näkymien näyttäminen tapahtuu UI-luokassa. Jokainen näkymä on toteutettu omana luokkanaan, ja vain yksi niistä on samaan aikaan näkyvissä. Eri näkymät sisältävät painikkeita, joiden klikkaaminen kutsuu Service-luokan metodeja.
Sekä hahmolistan että yksittäisen hahmon näkymät renderöivät näyttämäänsä tietoa sitä mukaa, kun käyttäjä luo tai poistaa uusia hahmoja tai muokkaa jonkun hahmon tietoja. Hahmolistanäkymästä kutsutaan sovelluksen metodia initialize_character_list, ja hahmon tietojen näkymästä kutsutaan muokattavasta kentästä riippuen esimerkiksi metodia initialize_name_field. Nämä metodit saavat sovelluslogiikalta päivittyneet tiedot, ja renderöivät näkymän uudelleen näiden tietojen perusteella.
classDiagram
User "1" <-- "*" Character
class User{
user_id
username
password
}
class Character{
character_id
name
ancestry
heritage
level
experience
hit points
}
Sovelluslogiikka koostuu luokista User ja Character. Molemmille on lisätty oma taulu SQLite3-tietokantaan.
Molemmilla luokilla on oma service-luokka pakkauksessa services, joka vastaa niiden toiminnallisista kokonaisuuksista.
Nämä luokat tarjoavat oman metodin jokaiselle käyttöliittymän toiminnolle, kuten hahmon rakentamiselle, sisäänkirjautumiselle tai hahmon nimen vaihtamiselle.
Nämä luokat pystyvät hakemaan ja tallettamaan tietoa käyttäjiin ja hahmoihin pakkauksen repositories kautta, joka sisältää omat luokat molempien taulujen käsittelyä varten. Näiden repository-luokkien toteutus injektoidaan service-luokille jälkimmäisten konstruktiokutsujen yhteydessä.
Pakkauksen repository luokkiin injektoidaan konstruktiokutsun yhteydessä yhteys tietokantaan moduulista database_connection.py, joka rakentaa yhteyden sovelluksen käynnistyessä ympäristömuuttujien tai config-tiedoston avulla.
Käyttäjien ja hahmojen tiedot tallennetaan SQLite3-relaatiotietokantaan. Sekä UserRepository
että CharacterRepository
-luokat tallentavat ja lukevat tietoa tietokannan tauluista users
ja characters
. Tämä tietokanta luodaan tai haetaan hakemistosta /data/, ja sen nimi määritetään .env tiedostossa ympäristömuuttujan avulla. Jos ympäristömuuttujaa ei ole määritetty, komento poetry run invoke create-database
luo tietokannan nimeltä "database.sqlite".
Käyttäjien tietokantataulu "users" sisältää seuraavat sarakkeet: user_id, username sekä password. User_id toimii samalla jokaisen käyttäjärivin yksilöllisenä tunnisteena, joka määritetään automaattisesti riville kun se lisätään ensimmäistä kertaa tauluun. Username on merkkijono, jolla käyttäjä kirjautuu sisään ja tunnistaa itsensä. Password on bcryptin avulla salattu ja suolattu salasana, jota käyttäjä käytyy kirjautumiseen. Koska salasanoja ei tallenneta tietokantaan sellaisenaan, sovelluslogiikka käyttää bcryptin checkpw()-metodia käyttäjän kirjautumisen tarkistamiseksi.
Hahmojen tietokantataulu "characters" sisältää seuraavat sarakkeet: character_id, creator_id, name, ancestry, heritage, level, experience ja hit_points. Suuri osa sarakkeista edustaa Pathfinder2-pelin sääntöihin perustuvia hahmojen ominaisuuksia. Character_id on hahmojen yksilöllinen tunnisteluku, kuten user_id "users"-taulukossa, ja se asetetaan hahmolle sen luomisen yhteydessä. Samoin kuin käyttäjien suhteen, character_id ei ole pakollinen parametri Character-entiteetille, sillä se asetetaan vasta SQL-taulun puolella. Creator_id viittaa hahmon luoneen käyttäjän "user_id":seen, joka saadaan tarkistamalla kirjautuneen käyttäjän tunnisteluku hahmon luonnin yhteydessä. Name, ancestry ja heritage ovat kaikki merkkijonoja, jotka kuvaavat pelin hahmoa ja vaikuttavat pelin sisäisiin vaihtoehtohin. Level, experience ja hit_points ovat numeroita, jotka myös toimivat osana Pathfinder-pelin sääntöjä.
Repository-luokkien testit tallentavat ja käsittelevät tietoa erillisestä testitietokannasta, jonka nimi on määritelty .env.test tiedostossa. Sovelluslogiikkaa testaavat Service-testit säilövät tietoa tietokannan sijasta keskusmuistiin testitiedostoissa määritellyn "FakeRepository"-luokan avulla. Integraatiotestit käyttävät samaa testitietokantaa kuin Repository-luokat. Nämä tietokannat tyhjennetään jokaisen testikerran alussa.
Sovellus tallentaa käyttäjien ja hahmojen tiedot .sqlite-tiedostoon. Käytettävän tiedoston nimi voidaan määrittää ympäristömuuttujaan .env-tiedostossa. Ilman erillisiä ympäristömuuttujia, varsinainen sovellus tallentaa tiedot data/database.sqlite tiedostoon, ja testit data/test-database.sqlite tiedostoon.
Kuvataan muutamaa päätoimintoa sekvenssikaavioiden avulla.
Sovelluksen kirjautumisnäkymän kirjoituskenttiin kirjoitetaan tunnuksen käyttäjätunnus ja salasana, jonka jälkeen klikataan "Login"-painiketta. Sitten sovelluksen kontrolli etenee seuraavalla tavalla:
sequenceDiagram
actor User
participant UI
participant UserService
participant UserRepository
User->>UI: click "Login" button
UI->>UserService: login("johnson9", "salis123")
UserService->>UserService: ("salis123").encode('utf-8')
UserService->>UserRepository: get_one_by_username("johnson9")
UserRepository-->>UserService: User
UserService->>bcrypt:checkpw(encoded("salis123"), User.password)
bcrypt-->>UserService: True
UserService-->>UI: User
UI->>UI:show_character_list_view()
Painikkeen painamiseen reagoiva tapahtumankäsittelijä kutsuu UserService-luokasta "login"-funktiota, jolle annetaan parametriksi näkymän syötekenttien sisältämät tekstit. Tämä funktio enkoodaa annetun salasanan utf-8 muotoon, jotta bcrypt-kirjasto pystyy käsittelemään sitä oikein.
UserService-olio kutsuu UserRepositoryltä funktiota "get_one_by_username", joka suorittaa Repositorio-olioon liitetylle tietokantayhteydelle SQL-kyselyn, mikä palauttaa käyttäjänimeä vastaavan User-entiteetin, joka sisältää myös salatun version käyttäjän salasanasta.
Jos käyttäjänimeä vastaavaa riviä ei löydy tietokannasta, tai jos bcryptin checkpw-metodi toteaa että kirjautumiseen käytetty salasana ei vastaa palautetun entiteetin salattua salasanaa, nostetaan virhe. Kaaviossa kuitenkin oletamme kirjautumisen onnistuvan, jolloin palautettu käyttäjä asetetaan kirjautuneeksi käyttäjäksi, ja palautetaan käyttöliittymälle. Lopulta käyttöliittymä siirtyy hahmolistanäkymään, joka sisältää kirjautuneen käyttäjän hahmot.
Sovelluksen kaikki toiminnallisuudet noudattavat samaa periaatetta. Käyttöliittymä kutsuu tapahtumankäsittelijältä sovelluslogiikalta jotakin metodia, ja sovelluslogiikka päivittää kirjautuneen käyttäjän, hahmolistan tai yksittäisen hahmon tilaa. Tämän jälkeen käyttöliittymä päivittää aktiivisen näkymän vastaavan logiikan päivittämää tilaa.