🏛️ Zašto su principi važni u e-Upravi?
Softverski sistemi u javnoj upravi (kao što su registri građana, sistemi za izdavanje dokumenata, portali e-Uprave, poreske evidencije) karakteristični su po tome što moraju raditi besprijekorno desetinama godina, moraju se stalno prilagođavati novim zakonima, i na njima rade timovi inženjera koji se s vremenom mijenjaju.
Pisanje koda koji "samo radi" u ovakvom okruženju je izuzetno opasno. Kôd mora biti napisan tako da ga je lako čitati, održavati, te nadograđivati u skladu s novim regulativama bez straha da će nešto puknuti u kritičnom sistemu za građane. Zato postoje programerski principi - set smjernica koje su godinama razvijali najiskusniji softver inženjeri kako bi spriječili propadanje koda u nečitljivi "špageti kôd".
📊 Brzi Pregled Principa
| Akronim | Puni Naziv | Suština u kontekstu Uprave |
|---|---|---|
| SOLID | (5 principa OOP-a) | Osnova za sisteme koji prate zakon: Lako dodavanje novih propisa bez rušenja postojećih servisa. |
| DRY | Don't Repeat Yourself | Ako jednom napišemo validaciju JMBG-a, ne kopiramo je u svakoj formi za zahtjeve. |
| KISS | Keep It Simple, Stupid | Ne komplikujte sistem općinskih taksi nečim što sistem čini pretjerano kompleksnim i skupim za održavanje. |
| YAGNI | You Aren't Gonna Need It | Ne pravite infrastrukturu za biometrijsko prepoznavanje ako zakon trenutno traži samo PIN kod. |
💎 SOLID Principi kroz prizmu Javnog Sektora
SOLID je akronim od pet osnovnih principa objektno-orijentisanog dizajna koje je promovisao Robert C. Martin (Uncle Bob). Kada se državni IT sistemi projektuju po ovim principima, izmjena Zakona o upravnom postupku neće značiti ponovno pisanje cijele aplikacije, već samo kreiranje novih minijaturnih blokova koji se nadovezuju na sistem.
S - Single Responsibility Principle (SRP)
Klasa treba da ima samo jedan razlog za promjenu, tj. jednu specifičnu odgovornost.
Zamislimo digitalni servis za odobravanje zahtjeva za izdavanje građevinske dozvole. Ako se proces
izdavanja dozvole promijeni (npr. treba novo obavještenje, ili se snima u drugačiju bazu),
trebali bismo mijenjati samo onaj dio koda koji je zadužen isključivo za taj aspekt.
// Loše: ProcessorZahtjeva klasa je zadužena za VIZUELNU validaciju, BAZU PODATAKA i EMAIL obavještenja!
public class ProcessorGradjevinskihDozvola
{
public void ObradiZahtjev(Zahtjev zahtjev, Gradjanin gradjanin)
{
// Odgovornost 1: Validacija (Šta ako zakon promijeni uvjete validacije?)
if(string.IsNullOrEmpty(zahtjev.BrojParcele))
throw new Exception("Broj parcele je obavezan po Zakonu čl. 42.");
// Odgovornost 2: Perzistencija / Rad s bazom (Šta ako pređemo sa SQL baze na neku državnu cloud platformu?)
using(var db = new VladinaBazaContext())
{
db.Zahtjevi.Add(zahtjev);
db.SaveChanges();
}
// Odgovornost 3: Obavještavanje (Šta ako država uvede obavezni SMS umjesto Email-a?)
var smtpClient = new SmtpClient("mail.vlada.gov.ba");
smtpClient.Send("[email protected]", gradjanin.Email, "Vaš zahtjev je zaprimljen", "...");
// Odgovornost 4: Auditiranje / Dnevnik rada
System.IO.File.AppendAllText(@"C:\Logs\UpravaLogs.txt", $"Zahtjev obradio ID: 1042 za parcela {zahtjev.BrojParcele}");
}
}
Problem: Ova klasa je postala ogroman "Silos". Ako se promijeni sistem logovanja, moramo dirati klasu koja direktno barata izdavanjem građevinskih dozvola! Time rizikujemo da slučajno pokvarimo logiku izdavanja dozvola dok "popravljamo" obavještenja.
// 1. Specijalista za Pravnu Validaciju
public class GradjevinskaDozvolaValidator
{
public bool JeLiZakonito(Zahtjev zahtjev)
{
return !string.IsNullOrEmpty(zahtjev.BrojParcele) && zahtjev.KatastarskaOpstina != null;
}
}
// 2. Specijalista za Arhiviranje (Bazu Podataka)
public class ZahtjevRepository
{
private readonly VladinaBazaContext _db;
public void Sacuvaj(Zahtjev zahtjev) { /* spašavanje... */ }
}
// 3. Specijalista za Odnose s Javnošću (Slanje poruka)
public class GradjaninNotifikator
{
public void PosaljiPotvrduPrijema(Gradjanin gradjanin) { /* slanje... */ }
}
// Koordinator postupka (Šef odjela) spaja ove specijaliste:!
public class SluzbenikZaDozvole
{
private readonly GradjevinskaDozvolaValidator _validator;
private readonly ZahtjevRepository _repozitorij;
private readonly GradjaninNotifikator _notifikator;
public void ObradiZahtjev(Zahtjev zahtjev, Gradjanin gradjanin)
{
if(!_validator.JeLiZakonito(zahtjev))
throw new Exception("Zahtjev nije u skladu sa zakonom.");
_repozitorij.Sacuvaj(zahtjev);
_notifikator.PosaljiPotvrduPrijema(gradjanin);
}
}
O - Open/Closed Principle (OCP)
Klase trebaju biti otvorene za proširenje, ali zatvorene za izmjenu postojećeg koda.
U upravi se pravila neprestano mijenjaju i donose se nove uredbe (npr. "Od 1. marta uvodimo novu taksu
za ubrzani proces, a
ukidamo plaćanje za penzionere"). Ako svaki put zbog toga moramo otvarati i prepravljati glavne klase za
obračun finansija, rizikujemo kreiranje bagova
u već testiranom i funkcionalnom sistemu.
public class ObracunAdministrativneTakse
{
public decimal IzracunajTaksu(Zahtjev zahtjev, KategorijaLica kategorija)
{
decimal osnovnaTaksa = 50.0m;
// Ako se sutra doda nova kategorija, MORAMO mijenjati ovaj metod!
if(kategorija.Tip == "Student")
return osnovnaTaksa * 0.5m; // 50% popusta
else if(kategorija.Tip == "Penzioner")
return 0m; // oslobođeni plaćanja
else if(kategorija.Tip == "UbrzaniPostupak")
return osnovnaTaksa * 2.0m; // dupla taksa
else if(kategorija.Tip == "RatniVojniInvalid")
return osnovnaTaksa * 0.2m; // 80% popusta
return osnovnaTaksa;
}
}
// Kreiramo zajedničko pravilo (Interfejs ili Apstraktnu klasu)
public abstract class PraviloZaTaksu
{
protected decimal OsnovnaTaksa = 50.0m;
public abstract decimal IzracunajIznos();
}
// Svaka kategorija ima svoju klasu. Ako zakon ostane isti za studente, nju nikada više ne diramo!
public class StandardnaTaksa : PraviloZaTaksu
{
public override decimal IzracunajIznos() => OsnovnaTaksa;
}
public class PenzionerOslobadjanje : PraviloZaTaksu
{
public override decimal IzracunajIznos() => 0m;
}
// 🌟 MAGIJA: Ako općinsko vijeće sutra izglasa novu taksu za online zahtjeve,
// DODAJE SE NOVA KLASA. Postojeći kod se NE MIJENJA!
public class OnlineObradaPopustTaksa : PraviloZaTaksu
{
public override decimal IzracunajIznos() => OsnovnaTaksa * 0.8m; // 20% popusta na e-uslugu
}
L - Liskov Substitution Principle (LSP)
Objekti bazne klase trebaju biti zamjenjivi objektima njenih naslijeđenih klasa bez da to izazove
pucanje aplikacije.
Zamislimo elektronsku arhivu dokumenata državne agencije. Svi dokumenti se arhiviraju, ali
"DržavnaTajna" dokumenti moraju proći drugačiju proceduru, ili uopšte ne bi smjeli biti digitalno
prenošeni.
public class JavnoDostupanDokument
{
public virtual string DobijLinkZaJavnost()
{
return "https://transparentnost.vlada.ba/doc/123";
}
}
// StrogoPovjerljivo nasljeđuje od JavnoDostupanDokument (greška u dizajnu!)
public class StrogoPovjerljivoDokument : JavnoDostupanDokument
{
public override string DobijLinkZaJavnost()
{
// Ovdje sistem puca jer ovaj dokument ne smije imati javni link!
throw new InvalidOperationException("Državna tajna se ne može javno dijeliti!");
}
}
Problem: Servis za objavu na portalu će proći kroz sve dokumente, očekujući da klasa `StrogoPovjerljivoDokument` radi isto kao i njena bazna klasa, a ona će u sred noći baciti Exception i srušiti server.
// Apstraktni pojam Dokumenta
public abstract class ArhivskiDokument
{
public string BrojProtokola { get; set; }
public DateTime DatumPrijema { get; set; }
}
// Interfejs samo za one dokumente koji smiju u javnost
public interface IJavnoObjavljiv
{
string DobijLinkZaJavnost();
}
public class ObicnaOdluka : ArhivskiDokument, IJavnoObjavljiv
{
public string DobijLinkZaJavnost() => $"https://transparentnost.vlada.ba/{BrojProtokola}";
}
public class TajniIzvjestaj : ArhivskiDokument
{
public int NivoKlasifikacije { get; set; }
// Nema "IJavnoObjavljiv", time je potpuno sigurno da niko ne može tražiti public link!
}
I - Interface Segregation Principle (ISP)
Klijente ne treba tjerati da zavise od interfejsa (metoda) koje ne koriste.
Jednostavno rečeno, napravite sitne, pametne interfejse, umjesto ogromnih univerzalnih interfejsa.
U javnoj upravi imamo različite uloge: šalterski službenici, inspektori na terenu, i šefovi odsjeka.
Nema smisla da šalterski radnik implementira funkciju koja se zove `ZakaziInspekcijskiNadzor`.
public interface IUpravniRadnik
{
void IzdajUvjerenje();
void ZaprimiZahtjev();
void OdobriBudzet();
void NapisiPrekrsajniNalog();
}
Problem: Šalterski službenik sad mora implementirati OdobriBudzet i
NapisiPrekrsajniNalog. Kako? Moraće napisati funkciju koja vraća
throw new Exception("Nemam ovlaštenja"). To garantuje haos u sistemu.
public interface IRadSaStrankama
{
void IzdajUvjerenje();
void ZaprimiZahtjev();
}
public interface IInspekcijskaOvlastenja
{
void NapisiPrekrsajniNalog();
}
public interface IBudzetskoOvlastenje
{
void OdobriBudzet();
}
// Konkretna implementacija se bazira samo na ovlastima
public class SalterskiSluzbenik : IRadSaStrankama
{
public void IzdajUvjerenje() { /* ... */ }
public void ZaprimiZahtjev() { /* ... */ }
}
public class GlavniInspektor : IRadSaStrankama, IInspekcijskaOvlastenja
{
public void IzdajUvjerenje() { /* ... */ }
public void ZaprimiZahtjev() { /* ... */ }
public void NapisiPrekrsajniNalog() { /* ... */ }
}
D - Dependency Inversion Principle (DIP)
Moduli visokog nivoa ne bi trebali zavisiti od modula niskog nivoa; oba bi trebala zavisiti od
apstrakcija.
Zamislimo državni "Centralni Registar Obavještenja". Ako vaša aplikacija direktno komunicira sa
BH Telecom SMS Gateway-em, šta će se dogoditi ako država na javnom tenderu sljedeće godine
odabere novog operatera? Moraćete mijenjati glavni kod.
public class SistemZaSlanjePoreznihRjesenja
{
// Cijeli sistem ZAVISI direktno od konkretne implementacije M:Tel API-ja!
private MTelSmsSender _smsServis = new MTelSmsSender();
public void ObavijestiPoreskogObveznika(string brojTelefona, string poruka)
{
_smsServis.PosaljiSms(brojTelefona, poruka);
}
}
// 1. Definisanje apstrakcije (Ugovora)
public interface IDrzavniSmsGateway
{
void PosaljiPoruku(string broj, string tekst);
}
// 2. Modul visokog nivoa bavi se samo interfejsom, briga njega ko je pobijedio na tenderu!
public class SistemZaSlanjePoreznihRjesenja
{
private readonly IDrzavniSmsGateway _smsGateway;
// Dependency Injection (Ubrizgavanje zavisnosti spolja)
public SistemZaSlanjePoreznihRjesenja(IDrzavniSmsGateway smsGateway)
{
_smsGateway = smsGateway;
}
public void ObavijestiPoreskogObveznika(string brojTelefona, string poruka)
{
_smsGateway.PosaljiPoruku(brojTelefona, poruka);
}
}
// 3. Konkretne implementacije se prosljeđuju aplikaciji (najčešće pri pokretanju servera)
public class TelecomSmsProvider : IDrzavniSmsGateway { ... }
public class EronetSmsProvider : IDrzavniSmsGateway { ... }
🧩 Separation of Concerns (SoC) - Odvajanje nadležnosti u arhitekturi
Dok su SOLID principi fokusirani na dizajn na nivou klasa, SoC se često primjenjuje na makro-arhitekturu sistema. Zamislite to kao podjelu vlasti na zakonodavnu, izvršnu i sudsku - svaka institucija drži svoj domen rada kako bi se spriječila zloupotreba, a sistem funkcionisao efikasno.
U razvoju e-Vladinih aplikacija (koje se npr. vrte na ASP.NET Core-u), strogo odvajamo "slojeve" aplikacije:
Tipični slojevi e-Servisa (N-Tier Architecture):
- UI / Presentation Layer (Frontend, Kontroleri): Samo formira vizuelni ekran (web stranicu za građane) na osnovu podataka. NEMA logiku izračuna ni konekcije na bazu.
- Business Logic / Service Layer: Provjerava pravo na ostvarenje socijalne pomoći na osnovu primanja. Ovdje su svi zakoni isprogramirani.
- Data Access Layer: Isključivo slanje SQL upita i konekcija ka Federalnoj Bazi Podataka. Nema prepoznavanja zakona ovdje.
Zašto je to važno za Javnu Upravu? Ako Vlada odluči da pored web portala kreira i mobilnu aplikaciju (novi Presentation sloj), Business Logika ostaje identična! Nema prepisivanja složenih zakonskih izračuna taksi ili prava, samo se naslanjamo na isti Service Layer.
♻️ DRY (Don't Repeat Yourself) u Državnim Sistemima
Princip kaže da svaki dio znanja, logike i podataka, unutar sistema mora imati jednu nedvosmislenu lokaciju na kojoj se održava.
Primjer u praksi javne uprave: Sistem za registraciju zahtjeva. Svaki put
kada neko aplicira za dječiji doplatak, borački dodatak, ili naknadu za nezaposlene, građanin mora
ukucati svoj JMBG.
JMBG u Bosni i Hercegovini ima tačno formulisan algoritam validacije (dan i godina rođenja, regija,
moduo kontrole brojeva).
Ako programer kopira logiku validacije (if (Jmbg.Length != 13) ... ) na 15 različitih
mjesta u programu, ta logika je osuđena na greške.
💡 Kako primijeniti DRY?
Napravite globalno dostupnu (ili injektovanu) komponentu JmbgValidatorServis.
Gdje god je potrebno unijeti matični broj, aplikacija poziva ovu jednu tačku istine. Ako se sistem
ikada počne
koristiti za strane državljane, ili se promijeni logika kontrole pogrešaka, mijenjate samo ovu jednu
klasu. Sve forme širom države
koje nasljeđuju, automatski rade ispravno.
😘 KISS (Keep It Simple, Stupid) - Jednostavnost ispred svega
Javne nabavke su često leglo prekomplikovanih zahtjeva, ali softver koji prati procedure mora biti koliko god je to moguće jednostavan. Ne koristite Microservise, Machine Learning pretrage ili asinkrone Event Bus arhitekture ako vam treba samo jedan ekran na kojem činovnik unese tri polja o novom ugovoru i to ode u bazu podataka.
Što je kod kompleksniji, teže ga je održavati i teže je "provaliti" u šta je greška kada podatak o plati zaposlenog ne valja. Koristite moćno i komplicirano oružje samo na teške probleme. Za administrativne obrade i unos teksta, prosta forma i CRUD pristup su optimalni.
🔮 YAGNI (You Aren't Gonna Need It) - Borba protiv predviđanja budućnosti
"Hajde da sada odmah implementiramo podršku za prepoznavanje šarenice oka korisnika kada se prijavljuje u katastar, možda će CIK to donijeti kao obavezu u zakonu za 5 godina!" - Klasična greška.
Pravilo YAGNI govori: Ne implementirajte funkcionalnosti u aplikaciju sve dok vam zakonito i striktno trenutni zahtjev to ne zatraži.. Dodavanjem unaprijed zamišljenih scenarija vi komplicirate sistem resursima koji VAM TRENUTNO (možda i nikada) ne trebaju. Kod se gomila, testovi postaju spori, učenje koda od strane novih programera je teže. Reagujte i dodajte funkcionalnosti kada dobijete stvarni funkcionalni zahtjev od Uprave.