Иди на текст

Ordify — Arhitektura

Detaljan tehnički opis Ordify platforme: servisi, modeli, API, obrasci i planovi.

1. Pregled servisa

Servis Port Baza Zaduženje
ordify-menu-service 5006 meni Meni: proizvodi, kategorije, cene, dostupnost
ordify-order-service 5007 porudzbine Biznis logika: korpa, porudžbine, pravila dostave
ordify-geofencing-service 8083 zone Zone dostave + brza provera lokacije (keš)
ordify-notification-service — — (planirano, Faza 2) WebSocket/SSE obaveštenja
ordify-licence-service — — (planirano, Faza 2) licenciranje instanci, subscription paketi
ordify-infrastructure — — Docker Compose, Keycloak, monitoring

Svi servisi: Quarkus 3.36.x, Java 21, PostgreSQL, Hibernate Panache, REST Jackson.


2. Strategija baza podataka

  • Database-per-service: svaki servis ima svoju logičku bazu — meni, porudzbine, zone (+ keycloak za Keycloak). Servisi komuniciraju isključivo preko API-ja, nikad preko deljene šeme (već tako radimo: order koristi batch REST ka menu-service).
  • Dev (Faza 1): jedan PostgreSQL kontejner (postgres:18.4) sa 4 baze, kreirane kroz db/init/01-create-databases.sql.
  • Prod (Faza 2+): PG instanca po servisu (K8s StatefulSet), nezavisni backup, nezavisno skaliranje. Aplikacija ne primećuje razliku — menja se samo JDBC URL.
  • Skaliranje (redosled poteza kad pojedinačna instanca poraste): kvalitetni indeksi → particionisanje/arhiviranje starih porudžbina → read replike (meni se čita ≫ piše) → PgBouncer (više podova) → sharding po tenantu. Bez sharding-a sada — on-prem model već deli podatke po klijentima (svaka instanca = prirodni shard); milijarda redova u jednoj bazi nije cilj dizajna.
  • Redis: ne koristi se kao keš. geofencing-service ima sopstveni in-memory keš (nanosekunde, bez mrežnog hopa); meni tabela je mala; order mora biti konzistentan. Redis (pub/sub) ulazi tek sa notification-service-om i multi-pod deploymentom (signal nove porudžbine svim podovima; alternativa: PostgreSQL LISTEN/NOTIFY).

3. Autentikacija — Keycloak ✅ (Faza 1)

Realm ordify — jedan ulaz za celu platformu (on-prem: svaka klijentska instanca ima svoj Keycloak sa svojim realm-om).

Klijenti:

Klijent Tip Namena
ordify-backend confidential Servisni pozivi (service account), kasnije server-to-server
ordify-admin public Admin panel (web, Faza 3)
ordify-kitchen public Kuhinjski ekran (web, Faza 3)
ordify-customer public Sajt kupca (web, Faza 3)

Role (realm-level): ADMIN, KITCHEN, CUSTOMER — dolaze u JWT kroz realm_access.roles; servisi ih proveravaju na endpoint-ima.

Dev korisnici: admin@ordify.dev (ADMIN), kitchen@ordify.dev (KITCHEN), customer@ordify.dev (CUSTOMER) — usernames: admin, kitchen, customer.

Realm import: keycloak/realm/ordify-realm.json (eksport iz verzije 26.7; format se menja između major verzija — eksportovati iz iste verzije u koju se importuje). Dev: mount + --import-realm. Prod: realm ugrađen u sliku pri build-u.

Zaštita endpoint-a (implementirano): - menu: POST/PUT/DELETE → ADMIN; GET-ovi i /api/products/batch javni (meni vidljiv bez prijave; batch je interni poziv order-service-a) - order: PUT /api/orders/{id}/status i GET /api/orders → KITCHEN/ADMIN; POST /api/orders javan (kupac poručuje bez prijave, do Faze 3) - geofencing: /api/admin/zones (CRUD) → ADMIN; /api/internal/zones/check javan (interno, poziva ga order-service)

Interni pozivi (order→menu, order→geofencing): za sada bez tokena — zaštita na nivou mreže (Docker interna mreža). Tokeni servis-po-servis dolaze sa servisnom diskoverijom (Faza 2).

Tehnologija: quarkus-oidc (application-type=service — bearer-only, verifikacija JWT potpisa lokalno preko JWKS; bez mrežnog poziva po zahtevu) + @RolesAllowed. Konfiguracija kroz env var-ove: OIDC_URL (auth-server-url, default http://localhost:8080/realms/ordify) i POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_PORT (datasource na infra postgres, baze meni/porudzbine/zone).


4. ordify-menu-service

Model: Product

Polje Tip Napomena
id Long Panache auto
name String Obavezan (validacija 422)
description String
price BigDecimal Cena
isAvailable boolean Vidljivo kupcima
isArchived boolean Soft delete
category String Za sada tekst; plan: zaseban entitet

API

Metod Putanja Opis
GET /api/products Svi ne-arhivirani proizvodi
GET /api/products/active Samo dostupni kupcima
POST /api/products/batch Proizvodi po listi ID-jeva (za order-service)
POST /api/products Kreiranje (validacija imena)
PUT /api/products/{id} Izmena (404 za arhivirane)
DELETE /api/products/{id} Meko brisanje → isArchived=true

Ključne odluke

  • Soft delete: brisanje samo postavlja isArchived; istorija porudžbina ostaje netaknuta.
  • Batch endpoint: omogućava order-service-u da povuče N proizvoda u jednom pozivu.
  • Testovi: 15 integracionih testova sa REST Assured (redosled kroz @Order).

5. ordify-order-service

Centralni biznis servis. Prima porudžbine, proverava zonu dostave, kontaktira menu-service za cene.

Modeli

Order | Polje | Tip | Napomena | |-------|-----|----------| | customerName | String | | | deliveryAddress | String | Prikaz na računu | | latitude / longitude | Double | GPS lokacija kupca | | note | String (500) | Napomena kupca | | totalPrice | BigDecimal | Računa se na osnovu trenutnih cena | | status | OrderStatus | Enumeracija | | createdAt | LocalDateTime | | | items | List\<OrderItem> | Cascade ALL |

OrderItem | Polje | Tip | Napomena | |-------|-----|----------| | productId | Long | Link ka proizvodu | | productName | String | Snapshot u trenutku porudžbine | | pricePaid | BigDecimal | Snapshot cene | | quantity | int | | | order | Order | ManyToOne (JsonIgnore) |

OrderStatus: KREIRANO → PRIHVAĆENO → U_DOSTAVI → DOSTAVLJENO; alternativno ODBIJENO.

API

Metod Putanja Opis
POST /api/orders Kreiranje porudžbine (geofencing + provera dostupnosti)
PUT /api/orders/{id}/status Promena statusa (string → enum)
GET /api/orders Sve porudžbine

Tok kreiranja porudžbine

  1. Validacija GPS koordinata (400 ako nedostaju)
  2. Geofencing provera — REST poziv ka geofencing-service (/api/internal/zones/check)
  3. Batch poziv ka menu-service (POST /api/products/batch) za cene
  4. Provera dostupnosti svakog proizvoda (400 ako je nedostupan)
  5. Snapshot imena/cena u OrderItem, računanje totalPrice
  6. Perzistencija sa statusom KREIRANO

REST klijenti

  • MenuServiceClient (configKey = "menu-api") → POST /api/products/batch
  • GeofencingClient (configKey = "geofencing-api") → GET /api/internal/zones/check?lat=&lng=

Konfiguracija URL-ova: quarkus.rest-client.menu-api.url (5006) i quarkus.rest-client.geofencing-api.url (8083).


6. ordify-geofencing-service

Samostalni servis za zone dostave. Ključna prednost: in-memory keš — provere lokacije nikada ne idu u bazu.

Modeli

  • DeliveryZone — name, isActive, coordinates (List\<ZonePoint>, EAGER, @OrderBy(sequence))
  • ZonePoint — lat, lng, sequence, zone (ManyToOne, JsonIgnore)
  • CachedZone — name, polygon (List\<LngLat>) — keš reprezentacija
  • LngLat — lng, lat

API

Metod Putanja Opis
GET /api/admin/zones Sve zone
POST /api/admin/zones Kreiranje (dodeljuje sequence tačkama)
PUT /api/admin/zones/{id} Izmena (preslikavanje postojećih tačaka po ID)
DELETE /api/admin/zones/{id} Brisanje (cascade + orphanRemoval)
GET /api/internal/zones/check?lat=&lng= Provera lokacije → {"isInside": true/false}

Keš mehanizam

  • zoneCache (volatile) + isDirty flag
  • Double-checked locking u ensureCacheIsFresh()
  • Invalidacija: CDI event ZoneChangedEvent (fire posle svake CRUD operacije), osluškivan sa @Observes(during = AFTER_SUCCESS) → isDirty = true
  • Provera: zoneCache.parallelStream().anyMatch(...) — paralelna provera kroz sve zone

GeofencingUtils

  • sortZone(DeliveryZone) — sortiranje temena poligona po uglu oko centra (centroid) — obezbeđuje ispravan redosled tačaka
  • isPointInPolygon(LngLat, List<LngLat>) — ray-casting algoritam

Testovi

GeofencingUtilsTest — tačka u poligonu (uspeh/propadanje) i sortiranje zone.


7. ordify-notification-service (planirano, Faza 2)

  • WebSocket/SSE veze sa frontendom (kuhinjski ekran)
  • Jedini zadatak: progurati obaveštenje o novoj porudžbini u milisekundi
  • Integracija: order-service šalje signal nakon kreiranja porudžbine
  • Multi-pod (K8s): signal se prosleđuje svim podovima preko Redis pub/sub (ili PG LISTEN/NOTIFY)

8. ordify-infrastructure (Faza 1, u izradi)

Centralni repo za: - docker-compose.yml + docker-compose.dev.yml (+ docker-compose.prod.yml nacrt) — PostgreSQL 18.4 + Keycloak 26.7 - db/init/ — skripte za kreiranje baza - keycloak/realm/ — realm eksport ordify-realm.json - monitoring/ — Prometheus v3.13.2 + Grafana 13.1 + alerting (Discord webhook) ✅ - CI/CD — GitHub Actions: build + test + native slike → GHCR ✅


9. Sigurnost i produkcija (TODO)

  • [x] Odluka: Keycloak 26.7, realm ordify, klijenti/role definisani
  • [x] OIDC zaštita servisa (quarkus-oidc + @RolesAllowed) — menu, order, geofencing
  • [x] Datasource na infra baze (meni/porudzbine/zone) kroz POSTGRES_* env var-ove
  • [ ] quarkus.hibernate-orm.database.generation → validate/none van DEV-a (trenutno drop-and-create)
  • [ ] Servisna diskoverija umesto hardkodovanih localhost URL-ova (Faza 2)
  • [ ] HTTPS (prod: reverse proxy / K8s ingress)
  • [ ] Backups strategija (prod, Faza 2)