Aplikacja internetowa a strona z szablonu

Agencje reklamowe budują serwisy na gotowym szablonie systemu CMS i zmieniają głównie wygląd. Taki serwis prezentuje treści, ale nie prowadzi procesu: nie zapisuje transakcji i nie egzekwuje reguł biznesowych. Gdy potrzebna jest interakcja i zaawansowana logika, praca przechodzi do software house, który projektuje model danych i warstwę serwerową razem z interfejsem.

Tworzenie aplikacji internetowej zaczyna się więc od pytania, jakie dane system ma zapisać i kto może je zmienić. Wygląd strony jest wtórny wobec tego, co dzieje się po kliknięciu przycisku. Tabela zestawia obie klasy rozwiązań.

KryteriumStrona z szablonu CMSAplikacja internetowa na zamówienie
LogikaWyświetlanie treści, formularz kontaktowyReguły biznesowe wykonywane na serwerze
DaneWpisy i podstrony w bazie CMSModel relacyjny w SQL Server z kluczami i ograniczeniami
IntegracjeWtyczki dostawcówInterfejsy REST projektowane pod istniejące systemy
UprawnieniaKilka ról edytorskichRole i zakresy danych wynikające z procesu
Zmiana wymagańWymiana szablonu lub wtyczkiModyfikacja kodu i migracja schematu bazy

Reguła: funkcja, która zapisuje dane i sprawdza warunki biznesowe, należy do serwera aplikacji. Skrypt w przeglądarce może ją tylko wywołać.

Dwie osoby pracujące z laptopami wśród chmur i pomarańczowych rozbłysków, ilustracja zespołu tworzącego aplikację webową
Zespół projektowy pracuje na tym samym repozytorium, a każda funkcja przechodzi przez przegląd kodu przed połączeniem z główną gałęzią.

Model dostarczania bywa różny. Platforma do tworzenia i wdrażania aplikacji biznesowych może działać jako oprogramowanie jako usługa, czyli SaaS z integracją aplikacji, albo zostać zainstalowana na serwerze przedsiębiorstwa. Decyzja wpływa na sposób aktualizacji i na to, kto odpowiada za kopie zapasowe.

Wybór platformy i języka programowania

Platforma i język wynikają z wymagań, a nie z mody. Aplikacja wewnętrzna firmy, która ma działać w sieci lokalnej i łączyć się z bazą SQL Server, naturalnie pasuje do C# i ASP.NET. Aplikacja z rozbudowanym interfejsem po stronie przeglądarki dodatkowo korzysta z JavaScriptu lub TypeScriptu. Stos technologiczny SoftwareStudio opiera się na C# i JavaScript z jQuery, a bazą jest SQL Server z językiem T-SQL.

Programiści zewnętrznej firmy powinni znać HTML i CSS oraz język skryptowy przeglądarki. Frameworki, takie jak React, Angular czy Vue.js, porządkują komponenty interfejsu, lecz wprowadzają własny łańcuch narzędzi i wymagają osobnej kompilacji. Przy aplikacji z kilkoma formularzami i tabelami często wystarcza lżejsze podejście z jQuery.

WarstwaRozwiązanieKiedy ma sens
PrzeglądarkaHTML i CSS z jQuery albo z frameworkiem SPAFramework SPA przy dużej liczbie stanów widoku, jQuery przy prostszych formularzach
Serwer aplikacjiC# i ASP.NET na serwerze IISŚrodowisko Windows Server z uwierzytelnianiem domenowym
DaneSQL Server, procedury składowane T-SQLTransakcje i raporty na jednym silniku
IntegracjeREST, w razie potrzeby wymiana plikówPołączenie z systemem ERP albo z systemem raportowym

Praktyczne kryterium wyboru to kompetencje zespołu, który będzie aplikację utrzymywał po wdrożeniu. Rzadki język w produkcji oznacza trudniejszą rekrutację i droższy serwis, dlatego stos ustala się razem z klientem. Opis podejścia do projektu na poziomie architektury zawiera artykuł o projektowaniu aplikacji webowych.

Projekt modelu danych i interfejsu

Przed pierwszą linią kodu powstaje specyfikacja: lista ról i słowników oraz przepływ statusów dokumentów. Interfejs projektuje się na podstawie tych ustaleń, więc każdy ekran odpowiada jednemu zadaniu użytkownika. Formularz z dwudziestoma polami zwykle oznacza, że proces nie został rozbity na etapy. Etap analizy opisuje szerzej tekst o analizie przedwdrożeniowej.

Ilustracja bazy danych: walce z danymi, wykresy i ikony chmury na niebiesko-pomarańczowym tle
Model danych powstaje przed interfejsem, bo to on ogranicza, co formularz może zapisać.

Model relacyjny zabezpiecza się ograniczeniami w bazie, a nie tylko walidacją w przeglądarce. Poniższy przykład, z nazwami poglądowymi, pokazuje tabelę zgłoszeń z kluczem obcym i ograniczeniem statusu.

CREATE TABLE dbo.Zgloszenie (
    ZgloszenieId  int IDENTITY PRIMARY KEY,
    KlientId      int          NOT NULL
        REFERENCES dbo.Klient (KlientId),
    Status        tinyint      NOT NULL DEFAULT 0
        CONSTRAINT CK_Zgloszenie_Status CHECK (Status IN (0, 1, 2)),
    Opis          nvarchar(500) NOT NULL,
    UtworzonoUtc  datetime2(0) NOT NULL DEFAULT SYSUTCDATETIME()
);

Ograniczenie CHECK odrzuci status spoza dozwolonej listy nawet wtedy, gdy zapis pochodzi z innego klienta interfejsu niż przeglądarka. Wersja z kluczem obcym uniemożliwia dodanie zgłoszenia do nieistniejącego klienta.

Implementacja i wywołania AJAX

Po stronie przeglądarki interakcja z serwerem odbywa się asynchronicznie. Strona nie przeładowuje się przy zapisie, a wynik operacji wraca jako dane w formacie JSON. Zapis z obsługą błędu wygląda następująco.

async function zapiszZgloszenie(dane) {
  const odp = await fetch('/api/zgloszenia', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(dane)
  });
  if (!odp.ok) {
    throw new Error('Zapis odrzucony, kod ' + odp.status);
  }
  return odp.json();
}

Kod odpowiedzi 400 oznacza błąd walidacji po stronie serwera, a 401 brak uwierzytelnienia. Interfejs pokazuje wtedy komunikat przy właściwym polu, zamiast ogólnego błędu. Ten sam punkt końcowy obsługuje później aplikacje mobilne, dlatego kontrakt danych opisuje się osobno. Łączenie aplikacji z innymi systemami omawia artykuł o integrowaniu aplikacji webowych.

Punkt wyjścia do budowy takiego interfejsu w środowisku .NET zawiera dokumentacja tworzenia interfejsu web API w ASP.NET Core w Microsoft Learn. Zakres usług SoftwareStudio obejmuje także oprogramowanie na zamówienie.

Zakres pierwszej wersji aplikacji

Pierwsza wersja obejmuje ten fragment procesu, który daje mierzalny efekt przy najmniejszej liczbie ekranów. Reszta trafia do kolejnych wydań. Wersja MVP pozwala sprawdzić założenia na prawdziwych danych, zanim rozbudowa podniesie koszt każdej zmiany. Kolejność funkcji ustala się według dwóch parametrów.

  • Częstość użycia - funkcja wykonywana codziennie trafia do pierwszego wydania, a raport kwartalny może poczekać.
  • Koszt błędu - operacja, której pomyłka generuje straty finansowe, dostaje pełną walidację i dziennik zmian już w pierwszej wersji.

Zakres zapisuje się jako lista historyjek użytkownika z kryteriami akceptacji. Przykładem jest zdanie: po zapisie zgłoszenie ma numer i status 0, a operator widzi je na liście w ciągu sekundy. Kryterium, które da się zmierzyć, zamienia odbiór w test, a nie w spór o interpretację.

Bezpieczeństwo aplikacji internetowej

Najczęstszym błędem w aplikacji z bazą SQL jest wstrzyknięcie kodu, które występuje, gdy tekst z formularza jest doklejany do zapytania. Zapytanie parametryzowane oddziela polecenie od danych, więc wartość z formularza nie zostanie wykonana jako kod. Przykład w C# z nazwami poglądowymi wygląda następująco.

using (var cmd = new SqlCommand(
    "SELECT ZgloszenieId, Status FROM dbo.Zgloszenie WHERE KlientId = @klient",
    conn))
{
    cmd.Parameters.Add("@klient", SqlDbType.Int).Value = klientId;
    using (var reader = cmd.ExecuteReader())
    {
        while (reader.Read()) { /* odczyt wiersza */ }
    }
}

Mechanizm i dobre praktyki opisuje dokumentacja o wstrzykiwaniu kodu SQL w Microsoft Learn. Ochrona nie kończy się na zapytaniach, bo każdy typ zagrożenia ma własny mechanizm obrony.

ZagrożenieMechanizm ochrony
Wstrzyknięcie kodu SQLZapytania parametryzowane i procedury składowane
Wstrzyknięcie skryptu do stronyKodowanie danych przy wyświetlaniu w HTML
Przejęcie sesjiPołączenie HTTPS i ciasteczko oznaczone jako HttpOnly
Nadmierne uprawnieniaKonto aplikacji w bazie z minimalnym zestawem uprawnień

Uprawnienia sprawdza serwer przy każdym żądaniu. Ukrycie przycisku w menu nie jest zabezpieczeniem, bo adres punktu końcowego można wywołać ręcznie. Hasła przechowuje się jako skróty z solą, a nie jawnym tekstem.

Wydajność zapytań i indeksy

Lista zgłoszeń klienta z filtrem po statusie jest wywoływana częściej niż jakakolwiek inna operacja, więc zasługuje na indeks. Bez niego SQL Server przegląda całą tabelę, a czas rośnie wraz z liczbą wierszy. Indeks na kolumnach filtra i sortowania pozwala silnikowi wykonać wyszukiwanie zamiast skanowania.

CREATE INDEX IX_Zgloszenie_Klient_Status
    ON dbo.Zgloszenie (KlientId, Status)
    INCLUDE (UtworzonoUtc);

SELECT ZgloszenieId, Status, UtworzonoUtc
FROM dbo.Zgloszenie
WHERE KlientId = @klient AND Status = 0
ORDER BY UtworzonoUtc DESC
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY;

Klauzula INCLUDE dopisuje kolumnę do liści indeksu, więc zapytanie nie sięga do tabeli po dodatkowe dane. Stronicowanie przez OFFSET i FETCH ogranicza liczbę wierszy przesyłanych do przeglądarki. Skuteczność indeksu sprawdza się w planie wykonania, a nie na podstawie przypuszczeń.

Repozytorium i przeglądy kodu przed wydaniem

Kod źródłowy aplikacji trzyma się w repozytorium z historią zmian. Gałąź główna zawiera wersję gotową do wydania, a każda funkcja powstaje w osobnej gałęzi i wraca do głównej dopiero po przeglądzie kodu. Recenzent sprawdza nie tylko styl, lecz także obsługę błędów oraz zgodność z ustaleniami z analizy.

Automatyczny serwer budowania kompiluje projekt przy każdej zmianie i uruchamia testy jednostkowe. Zepsuta kompilacja blokuje połączenie gałęzi, więc błąd wykrywa się w minutach, a nie na etapie odbioru. Gotowy pakiet wdrożeniowy jest tym samym plikiem w środowisku testowym i produkcyjnym, zmieniają się tylko ustawienia połączeń.

PraktykaJaki problem usuwa
Przegląd kodu przed połączeniem gałęziBłędy logiczne i niespójności, których autor nie widzi we własnym kodzie
Automatyczna kompilacja i testyRegresje wprowadzone przez zmianę w innym module
Skrypty wdrożeniowe w repozytoriumRęczne pomyłki przy kopiowaniu plików na serwer
Wersjonowane migracje bazyRozbieżność schematu między środowiskami

Dobrą praktyką jest też plan wycofania. Przed wdrożeniem zapisuje się poprzednią wersję pakietu i kopię bazy, by w razie błędu przywrócić poprzedni stan w kilkanaście minut. Aplikacje mobilne korzystające z tego samego interfejsu, opisane w artykule o tworzeniu aplikacji mobilnych, wymagają wtedy zachowania zgodności wstecznej punktów końcowych.

Reguła: zmiana kontraktu interfejsu, na przykład usunięcie pola z odpowiedzi, wchodzi w osobnym wydaniu po okresie przejściowym, w którym działają obie wersje.

Testowanie i wdrożenie

Aplikacja przed wdrożeniem przechodzi kilka rodzajów testów. Każdy z nich sprawdza inną klasę błędów i działa na innym poziomie kosztu.

Rodzaj testuCo sprawdzaKiedy się uruchamia
JednostkowyPojedyncza funkcja z regułą biznesowąPrzy każdej zmianie kodu
IntegracyjnyWspółpraca aplikacji z bazą i interfejsamiPrzed połączeniem gałęzi
AkceptacyjnyZgodność z wymaganiami klienta na danych testowychPrzed wdrożeniem produkcyjnym
ObciążeniowyCzas odpowiedzi przy wielu równoległych użytkownikachPrzy większej zmianie zapytań

Wdrożenie przebiega w dwóch środowiskach: testowym, na kopii struktury bazy, i produkcyjnym. Zmiany schematu wykonuje się skryptami migracji zapisanymi w repozytorium, dzięki czemu środowiska nie rozchodzą się w czasie. Przed uruchomieniem produkcyjnym wykonuje się kopię bazy i sprawdza jej odtworzenie na osobnym serwerze.

Środowisko testowe wymaga danych zbliżonych do produkcyjnych, bo błędy wydajności i formatowania ujawniają się dopiero na realnych wolumenach. Kopię danych klienta przenosi się tam po zanonimizowaniu pól osobowych, na przykład nazwisk i numerów telefonów. Testy akceptacyjne prowadzi wskazany przedstawiciel klienta według listy przypadków z analizy, a wynik każdego przypadku jest zapisywany w protokole odbioru.

Ostatnim elementem wdrożenia jest szkolenie użytkowników i przekazanie instrukcji. Osoba odpowiedzialna po stronie klienta dostaje konto administracyjne i opis procedur, na przykład zakładania użytkowników czy sprawdzania dziennika błędów. Bez tego pierwsze zgłoszenia po uruchomieniu dotyczą obsługi, a nie usterek aplikacji.

Reguła: kopia zapasowa, której odtworzenia nie sprawdzono, nie jest zabezpieczeniem. Test przywrócenia wchodzi do listy odbiorowej wdrożenia.

Hosting i utrzymanie aplikacji

Po wdrożeniu aplikacja działa na serwerze należącym do firmy programistycznej albo do klienta. Przeglądarka wysyła zapytanie, serwer odpowiada, a użytkownik potrzebuje tylko urządzenia z przeglądarką i zwykle dostępu do Internetu. Część funkcji może działać także bez sieci, jeśli aplikacja zapisuje dane lokalnie i synchronizuje je później.

SoftwareStudio wskazuje w opisie oferty chmurę prywatną na platformie VMware jako jedno z miejsc uruchamiania aplikacji. Wirtualizacja pozwala przenieść maszynę na inny host bez zmiany aplikacji, co upraszcza prace serwisowe. Szersze omówienie środowiska serwerowego zawiera artykuł o dedykowanych aplikacjach webowych.

Utrzymanie obejmuje poprawki błędów i aktualizacje bibliotek, a także kontrolę miejsca w bazie. Umowa powinna określać czas reakcji na zgłoszenie oraz sposób przekazania kodu źródłowego. Sam proces wytwarzania i rozwoju systemów biznesowych opisano w tekście o programowaniu aplikacji biznesowych, a rolę firmy programistycznej w całym cyklu przedstawia strona software house.

Element umowy utrzymaniowejCo powinien określać
Czas reakcjiGodziny od zgłoszenia do pierwszej odpowiedzi, osobno dla awarii i drobnych uwag
Kod źródłowySposób przekazania repozytorium i dokumentacji technicznej
Kopie zapasoweCzęstotliwość i miejsce przechowywania oraz test odtworzenia
Zakres zapisów, które porządkują odpowiedzialność po wdrożeniu aplikacji.