MODUL 1 - LEKCIJA 6

Design Patterns (Dizajn Obrasci) u Javnoj Upravi

Provjerena softverska rješenja za masovne sisteme. Naučite implementaciju standardiziranih obrazaca kroz primjere iz portala e-Uprave, sudskih registara i općinskih usluga.

⏱️ Trajanje: ~60 min 📚 Nivo: Napredni 🎯 Koncepti: Creational, Structural, Behavioral Patterns

🏗️ Šta su Design Patterns?

Za razliku od pravih biblioteka koda (poput jQuery i Bootstrap-a) koje možete samo ugraditi i koristiti, Dizajn Obrasci (Design Patterns) su opisana i teoretski dokazana rješenja za softverske probleme koji se stalno ponavljaju. Oni su nastali iz višedecenijskog iskustva najboljih arhitekata softvera u svijetu (na bazi knjige "Gang of Four" iz 1994).

Državne institucije poput ministarstava, općina i poreskih uprava obično razvijaju gigantske platforme koje istovremeno moraju rukovati porezima, radnicima, građanima i budžetima. Dizajn obrasci su jezik koji omogućava inženjerima da kažu: "Koristit ćemo Singleton za bazu" umjesto objašnjavanja koda 15 minuta.

📊 Klasifikacija Obrazaca

Kategorija Svrha u Javnoj Upravi Patterni
Creational (Kreativni) Kako ispravno instancirati i "stvoriti" kompleksne državne biltene, forme i jedinstvene servise. Singleton, Factory, Builder
Structural (Strukturni) Kako svezati "prastari" IT sistem katastra i novi web portal zajedno kroz standardne "konektore". Adapter, Decorator, Facade
Behavioral (Ponašajni) Kako obavijestiti sto različitih odsjeka (bolnice, poreznu, sudove) kad se desi promjena adrese. Observer, Strategy, Command

✨ Creational Patterns - Kako kreirati strukture

Osnovna namjena im je optimizacija i sigurnost procesa instanciranja klasa klasičnom new Klasa() sintaksom.

1. Singleton Pattern - Vladina Konfiguracija

Problem: Vaša aplikacija za e-Katastar mora komunicirati s CIK-ovom bazom. Server prima hiljade zahtjeva. Ako za svakog korisnika aplikacija ponovo kreira proces otvaranja, očitavanja konfiguracije o taksama, radnom vremenu servisa i API ključevima, server će usporiti i srušiti se.
Rješenje: Singleton garantuje da postoji APSOLUTNO JEDNA (i jedina) instanca objekta koja se dijeli svim korisnicima aplikacije u memoriji na cijelom serveru.

📜 Singleton - Centralni Konfiguracioni Portal
public sealed class EUpravaKonfiguracija
{
    private static EUpravaKonfiguracija _instance = null;
    private static readonly object _lock = new object();
    
    // Ključni podaci sistema (Učitani iz baze SAA)
    public string GlavniTrekorRacun { get; private set; }
    public decimal TaksaZaElektronskeUsluge { get; private set; }
    public bool SistemURezimOdrzavanja { get; private set; }
    
    // Privatni konstruktor - niko izvana ne može napisati 'new EUpravaKonfiguracija()'
    private EUpravaKonfiguracija()
    {
        // Simulacija učitavanja skupe baze (Treba sekunda vremena)
        GlavniTrekorRacun = "338-900-220-449-1122";
        TaksaZaElektronskeUsluge = 5.00m;
        SistemURezimOdrzavanja = false;
    }
    
    // Jedna globalna ulazna tačka za sve građane i kontrolere servera!
    public static EUpravaKonfiguracija Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock) // Blokira paralelan pristup iz različitih requestova
                {
                    if (_instance == null)
                        _instance = new EUpravaKonfiguracija();
                }
            }
            return _instance;
        }
    }
}

// U svakom kontroleru e-uprave pozivamo jednostavno:
var config = EUpravaKonfiguracija.Instance;
Console.WriteLine(config.GlavniTrekorRacun); // Neće se nikad dupla u memoriji kreirati.

2. Factory Method Pattern - Mašina za Štampanje Dokumenata

Problem: Građanin putem portala može zatražiti Rodni List, Vjenčani list ili Uvjerenje o nekažnjavanju. Svaki od ovih zahtijeva potpuno različitu proceduru popunjavanja i konektovanja na druge državne baze podataka. Kôd bi bio noćna mora ispunjena ogromnim if-else blokovima prilikom generisanja onog pravog fajla na kraju.

📜 Factory Method - Zvanični Dokumenti
// 1. Interfejs općenitog rješenja:
public interface IZvanicniDokument
{
    void GenerisiPdf(string jmbg);
}

// 2. Specifične produkcione klase
public class RodniList : IZvanicniDokument
{
    public void GenerisiPdf(string jmbg)
    {
        // Logika pristupa matičnim knjigama rođenih i IDDEEA bazi
        Console.WriteLine($"Kreiranje izvatka iz matične knjige rođenih za {jmbg}");
    }
}

public class UvjerenjeKrivicno : IZvanicniDokument
{
    public void GenerisiPdf(string jmbg)
    {
        // Logika pristupa bazi MUP-a i sudskim registrima
        Console.WriteLine($"Provjera krivične evidencije sudova za {jmbg}");
    }
}

// 3. FACTORY - "Šalterski" sistem koji odlučuje i dispečeruje zahtjev!
public class DokumentFactory
{
    public static IZvanicniDokument KreirajDokument(string vrstaDokumenta)
    {
        return vrstaDokumenta.ToUpper() switch
        {
            "RODNI_LIST" => new RodniList(),
            "KRIVICNO_UVJERENJE" => new UvjerenjeKrivicno(),
            "VJENCANI_LIST" => new VjencaniList(),
            _ => throw new ArgumentException("Općina ne izdaje ovaj dokument.")
        };
    }
}

// KORIŠTENJE: API endpoint samo primi string i JMBG, i zove factory!
IZvanicniDokument dokument = DokumentFactory.KreirajDokument("RODNI_LIST");
dokument.GenerisiPdf("1208990150011");

3. Builder Pattern - Javne Nabavke

Problem: Dosje o Javnoj Nabavci je kompleksan. Može, ali ne mora imati komisije, bankovne garancije, certifikate, žalbe, i tehničku specifikaciju na preko 100 stranica. Praviti new JavnaNabavka(..., ..., ..., ..., null, null, null, ...) je potpuno haotično.

📜 Builder - Kreiranje Tendera
public class JavnaNabavkaTender
{
    public string NazivProjekta { get; set; }
    public decimal ProcijenjenaVrijednost { get; set; }
    public bool TraziBankovnuGaranciju { get; set; }
    public string Komisija { get; set; }
    public int RokIsporukeDani { get; set; }
}

// BUILDER
public class TenderBuilder
{
    private JavnaNabavkaTender _tender = new JavnaNabavkaTender();
    
    public TenderBuilder PostaviNaziv(string naziv)
    {
        _tender.NazivProjekta = naziv;
        return this; // fluent interface
    }
    
    public TenderBuilder SaBudzetom(decimal budzet)
    {
        _tender.ProcijenjenaVrijednost = budzet;
        return this;
    }
    
    public TenderBuilder DodajGaranciju()
    {
        _tender.TraziBankovnuGaranciju = true;
        return this;
    }
    
    public JavnaNabavkaTender IzgradiTender()
    {
        if(string.IsNullOrEmpty(_tender.NazivProjekta))
            throw new Exception("Tender nema naziv!");
        return _tender;
    }
}

// Lako čitanje za inženjera, potpuno jasno šta se kreira
var tenderZaAsfalt = new TenderBuilder()
    .PostaviNaziv("Asfaltiranje Ulice Maršala Tita 2026")
    .SaBudzetom(300000m)
    .DodajGaranciju() // Jasno na prvu!
    .IzgradiTender();

🧱 Structural Patterns - Konekcije Sistema

1. Adapter Pattern - Modernizacija starih Uprava

Problem: Katastarske knjige (zemljišno knjižni izvadci) su na starom Mainframe IBM serveru iz 1998. godine. Vraćaju podatke isključivo u XML formatu. Novi web portal za građane i investitore očekuje isključivo brzoleteći JSON API format. Moramo napraviti spregu ove dvije strane.

📜 Adapter - JSON/XML konverzija
// Stari katastar daje prastari XML
public class StariZkKatastar
{
    public string PreuzmiSveParceleXml(int opstinaId)
    {
        return "<parcele><parcela kbr='345'><vlasnik>Država BiH</vlasnik></parcela></parcele>";
    }
}

// Novi Portal želi JSON
public interface INoviKatastarskiPortal
{
    string DohvatiParceleJson(int opstinaId);
}

// ADAPTER: Stoji na sredini
public class KatastarAdapter : INoviKatastarskiPortal
{
    private readonly StariZkKatastar _stariSistem;
    
    public KatastarAdapter(StariZkKatastar stari)
    {
        _stariSistem = stari;
    }
    
    public string DohvatiParceleJson(int opstinaId)
    {
        string siroviXml = _stariSistem.PreuzmiSveParceleXml(opstinaId);
        
        // Logika pretvaranja u lagani REST JS objekat
        var xmlElementi = XDocument.Parse(siroviXml);
        // ... C# konverzija ...
        return JsonSerializer.Serialize(new { Vlasnik = "Država BiH", Broj = "345" });
    }
}

2. Decorator Pattern - Nadodavanje sigurnosti i zaštita JMBG

Omogućava dinamičko lijepljenje novih mogućnosti funkcionalnosti. U slučaju pristupa ličnim podacima građana: Ili pravite pristup, Ili pristup sa zapisivanjem pristupa, Ili pristup sa snažnom dvostrukom provjerom!

📜 Decorator - Sigurnosne ljuske (Onion Layers) oko pristupa Podacima
public interface IGrađanskiServis
{
    string DohvatiPodatkeOsobe(string jmbg);
}

// Osnova - Samo ide u bazu i čita.
public class BazniNacionalniServis : IGrađanskiServis
{
    public string DohvatiPodatkeOsobe(string jmbg) => "{ ime: 'Sanja', prebivaliste: 'Sarajevo' }";
}

// Decorator 1: Praćenje i LOGOVANJE zbog Zakona o zaštiti ličnih podataka
public class AuditLogDecorator : IGrađanskiServis
{
    private readonly IGrađanskiServis _inner;
    public AuditLogDecorator(IGrađanskiServis inner) { _inner = inner; }
    
    public string DohvatiPodatkeOsobe(string jmbg)
    {
        File.AppendAllText("pristupi.log", $"[!] U {DateTime.Now} pristupljeno dosijeu JMBG: {jmbg}\n");
        return _inner.DohvatiPodatkeOsobe(jmbg); // PROSJEĐUJE POZIV!
    }
}

// Decorator 2: Autorizacija - ima li korisnik pristup?
public class SecurityCheckDecorator : IGrađanskiServis
{
    private readonly IGrađanskiServis _inner;
    public SecurityCheckDecorator(IGrađanskiServis inner) { _inner = inner; }
    
    public string DohvatiPodatkeOsobe(string jmbg)
    {
        if(!TrenutniKorisnik.ImaDozvolu("TAJNI_PODACI"))
            throw new Exception("Neovlašten pristup bazi CIPS-a!");
            
        return _inner.DohvatiPodatkeOsobe(jmbg);
    }
}

// Kada pokrećemo web aplikaciju, dekorišemo je kao BABUŠKE!
IGrađanskiServis cipsAPI = new BazniNacionalniServis();
cipsAPI = new AuditLogDecorator(cipsAPI);
cipsAPI = new SecurityCheckDecorator(cipsAPI);

// Kada pozovemo DohvatiPodatke, sistem ĆE PRVO ići u Security, pa u AuditLog, pa tek u Bazu!
string info = cipsAPI.DohvatiPodatkeOsobe("1234");

3. Facade Pattern - Objedinjeni Šalter

Tipičan problem BiH administracije je kada građanin otvara kompaniju. Registracija te firme radi putem Poreske Uprave (ID broj), Sudskog Registra (Rješenje), Zavoda za Statistiku i raznih obavijesti. Da građanin (ili web portal) to ne mora rješavati u 20 koraka koda, kreira se FACADE (Fasada) koja "krije" haos pozadi u vidu samo jedne jednostavne funkcije: OtvoriPreduzece("Moja Firma doo");.

🚦 Behavioral Patterns - Slanje poruka i algoritmi

1. Observer Pattern - Registar Promjena Adrese

Kada se građanin putem e-Servisa preseli iz općine Ilidža u općinu Novi Grad (MUP CIPS), u trenutku spremanja te jedne promjene aplikacija mora poslati okidač mnogim institucijama da ga uključe/skinu s budžeta i evidencija.

📜 Observer - Reagovanje na Promjenu Mjesta
// 1. Interfejs promatrača
public interface IPrebivalistePromatrac
{
    void NaPromjenuAdreseEvent(string jmbg, string novaOpstina);
}

// 2. Državne Službe koje ČEKAJU događaj
public class PoreznaUpravaObserver : IPrebivalistePromatrac
{
    public void NaPromjenuAdreseEvent(string jmbg, string novaO) => 
        Console.WriteLine($"POREZNA: Premještam poreski karton u poslovnicu {novaO}");
}
public class CikIzboriObserver : IPrebivalistePromatrac
{
    public void NaPromjenuAdreseEvent(string jmbg, string novaO) => 
        Console.WriteLine($"CIK: Biračko mjesto građanina {jmbg} promijenjeno!");
}

// 3. Glavni Sistemski MUP HUB (Izdavač poruke)
public class SistemPrebivaliste
{
    private List<IPrebivalistePromatrac> _pretplatnici = new();

    public void DodajOsluskivaca(IPrebivalistePromatrac o) => _pretplatnici.Add(o);

    public void ZavediPromjenuMup(string jmbg, string opstina)
    {
        Console.WriteLine($"-- MUP Ažurirao Centralnu Bazu za {jmbg} --");
        // OKIDAČ: Sve zainteresovane obavjesti!
        foreach(var sluzba in _pretplatnici)
            sluzba.NaPromjenuAdreseEvent(jmbg, opstina);
    }
}

2. Strategy Pattern - Plaćanje komunalija i zakupa

Građanin koji ima obavezu rente od općine može to platiti na desetine načina - klasičnim e-bankingom, kreditnom karticom direktno na portalu države, ili klasičnom papirnom uplatnicom sa barkodom kojeg ponese na šalter pošte.

📜 Strategy - Odabir Naplate
public interface INacinPlacanjaTakse
{
    bool IzvrsiPlacanje(decimal iznos, string referenca);
}

public class VizaKarticaNaplata : INacinPlacanjaTakse
{
    public bool IzvrsiPlacanje(decimal iznos, string r) => true; // Spaja se na Stripe Ccard
}
public class GenerisiUplatnicuBarkod : INacinPlacanjaTakse
{
    public bool IzvrsiPlacanje(decimal iznos, string r) 
    {
        Console.WriteLine("Kreiran PDF sa barkodom. Idi u banku.");
        return true; 
    }
}

// Web Aplikacija tokom 'Kase' samo proslijedi state. Niti jedan IF!
public class SalterKasa
{
    public void NaplatiDug(decimal ukupanDug, string ugovor, INacinPlacanjaTakse strategijaPlacanja)
    {
        strategijaPlacanja.IzvrsiPlacanje(ukupanDug, ugovor);
    }
}

3. Command Pattern - Praćenje Administrativnih Rješenja

Kada sud ili visoko vijeće donesu nalog za smjenu ministra ili odobrenje velikog ugovora, u bazi to nije jednostavna `UPDATE` izmjena. Potez može biti predmet sumnje za tri mjeseca zašto je to odobreno. Command obrazac generiše puni entitet te administrativne narědbe koji se pohranjuje u Bazi, sadrži ko ga je izdao, kako glasi, a po rješenju Suda omogućava momentalni UNDO efekt na prošlost.

🎯 Zaključak: Kako u Upravi?

  • Zakon prvenstveno - Ako zakon nalaže da postoji jedna agencija s unikatnim PIB-om (Jedinstvenim brojem), onda se arhitektura softvera MORA na to ugledati (Singleton/Factory).
  • Zaštitite Podatke u Arhitekturi - Enkapsulacija spriječava miješanje ličnih podataka, a Decorator obrasci nam jamče autorizacijsku strogost.