Zaawansowane projektowanie aplikacji mobilnych
Rok akademicki: 2026/27
Projekt zespołowy: Wieloplatformowe aplikacje mobilne Offline-First w ekosystemie Expo
Prowadzący: Tomasz Goluch Wersja: 1.0
Wprowadzenie i organizacja zajęć
Cel: Zaprojektowanie, implementacja, przetestowanie oraz wdrożenie produkcyjnego instalatora wieloplatformowej aplikacji mobilnej w paradygmacie Offline-First, opartej o ekosystem Expo (React Native) oraz Czystą Architekturę (Clean Architecture).
Projekt realizowany jest w zespołach 3–4 osobowych w wymiarze 8 godzin dydaktycznych podzielonych na dwa bloki stacjonarne oraz asynchroniczną pracę projektową w repozytorium Git:
- Spotkanie 1 (4h lekcyjne – Wprowadzenie i
warsztat):
- Omówienie założeń architektonicznych i standardów inżynierskich na bazie Instrukcji laboratoryjnej "Stop Scout".
- Formowanie zespołów, wybór wariantu domenowego oraz formalny podział ról.
- Inicjalizacja repozytorium Git, konfiguracja reguł gałęzi, szablonów Pull Requestów oraz szkieletu katalogów Clean Architecture.
- Praca projektowa (Asynchroniczna realizacja w Git):
- Implementacja warstw domeny, infrastruktury, bazy SQLite i interfejsu użytkownika.
- Prowadzenie rewizji kodu (Code Review) w ramach zadań przypisanych do poszczególnych ról.
- Implementacja automatycznych testów jednostkowych w Jest oraz
wygenerowanie pliku instalacyjnego
.apk.
- Spotkanie 2 (4h lekcyjne – Finałowy odbiór i
ocena):
- Prezentacja działania samodzielnego instalatora
.apkna fizycznym telefonie (klonowanym na ekran przezscrcpy). - Weryfikacja działania w trybie offline (odcięcie łączności sieciowej).
- Audyt inżynierski historii repozytorium Git i recenzji PR.
- Weryfikacja indywidualnej znajomości kodu przez każdego członka zespołu.
- Prezentacja działania samodzielnego instalatora
💻 Prezentacja ekranu telefonu (scrcpy):
Podczas odbioru projektu wymagane jest udostępnianie ekranu fizycznego smartfona na monitorze komputera za pomocą narzędzia scrcpy (klonowanie obrazu przez kabel USB z zerowym opóźnieniem). Aplikacja musi działać w 100% autonomicznie jako zainstalowany pakiet.apk– serwer deweloperski Metro na komputerze musi pozostać wyłączony.
Role w zespole projektowym i kultura inżynierska
Cel: Równomierny podział odpowiedzialności technicznej, eliminacja konfliktów scalania (merge conflicts) oraz egzekwowanie dobrych praktyk inżynierii oprogramowania.
Czysta Architektura w sposób naturalny separuje warstwy systemu na niezależne moduły. Każdy student obejmuje jedną z poniższych ról technicznych i odpowiada za powierzony mu obszar:
┌────────────────────────────────────────────────────────────────────────┐
│ STRUKTURA ODPOWIEDZIALNOŚCI │
├────────────────────────────────────────────────────────────────────────┤
│ [UI & State Lead] --> app/, components/, src/store/ │
│ [Domain & QA Lead] --> src/domain/, src/use-cases/, __tests__/ │
│ [Data & Security Lead] --> src/infrastructure/storage/, api/, auth │
│ [Hardware & DevOps Lead] --> src/infrastructure/location/, webview, │
│ app.json, eas.json, CI/CD │
└────────────────────────────────────────────────────────────────────────┘
1. Inżynier Domeny & Jakości (Domain & QA Lead)
- Odpowiedzialność: Czysta logika biznesowa,
orkiestracja procesów oraz testy automatyczne. Kod w tym obszarze nie
może posiadać ani jednego importu z bibliotek
react-native,expoczy silnika bazodanowego. - Katalogi:
src/domain/,src/use-cases/,__tests__/. - Zadania:
- Definicja niemutowalnych encji domenowych TypeScript
(
entities/) i interfejsów repozytoriów (repositories/). - Implementacja czystych funkcji matematycznych i biznesowych (np. kalkulator dystansu geograficznego Haversine, normalizator linii TRISTAR).
- Implementacja przypadków użycia (
use-cases/) orkiestrujących przepływ danych. - Konfiguracja środowiska Jest z presetem
jest-expooraz implementacja testów jednostkowych weryfikujących reguły brzegowe, mocki repozytoriów (jest.fn()) i symulację upływu czasu (jest.useFakeTimers()).
- Definicja niemutowalnych encji domenowych TypeScript
(
2. Inżynier Danych & Bezpieczeństwa (Data & Security Lead)
- Odpowiedzialność: Trwałość danych, relacyjna baza SQLite, bezpieczeństwo kryptograficzne oraz odporna na błędy integracja sieciowa.
- Katalogi:
src/infrastructure/storage/,src/infrastructure/api/, moduły autoryzacji. - Zadania:
- Projekt schematu bazy danych w
expo-sqlite(tabele, klucze obce, indeksy, tryb WAL). - Prawidłowa inicjalizacja bazy asynchronicznie (zakaz blokującego
top-level
await). - Konstrukcja optymalnych zapytań zakresowych SQL (filtrowanie prostokątem współrzędnych Bounding Box).
- Bezpieczne haszowanie haseł za pomocą
bcryptjsz jawną rejestracją generatora losowego zexpo-cryptodla silnika Hermes. - Implementacja klientów REST API, bezpieczne parsowanie dat i czasów
w formacie ISO-8601 UTC oraz obsługa wartości pustych
(
null/undefined).
- Projekt schematu bazy danych w
3. Inżynier UI/UX & Reaktywności (UI & State Lead)
- Odpowiedzialność: Warstwa prezentacji, ergonomia interfejsu, płynność list, nawigacja oraz reaktywny stan globalny.
- Katalogi:
app/(Expo Router),components/,src/store/. - Zadania:
- Struktura plików tras w Expo Routerze (layouty, pasek zakładek
(tabs), modale). - Reużywalne, typowane komponenty interfejsu z bezwzględnym
przestrzeganiem zasad React Native (brak tekstu poza znacznikami
<Text>). - Wydajne listy
FlatListzabezpieczone przed zbędnym renderowaniem. - Magazyn Zustand z implementacją wzorca Optimistic UI (natychmiastowy update widoku, asynchroniczny zapis do SQLite w tle i automatyczny rollback w razie awarii).
- Sprzętowo akcelerowane animacje modułu
Animatedz wymuszoną flagąuseNativeDriver: true.
- Struktura plików tras w Expo Routerze (layouty, pasek zakładek
4. Inżynier Integracji Natywnej & DevOps (Hardware & DevOps Lead)
- Odpowiedzialność: Podsystemy sprzętowe urządzenia (GPS), komunikacja międzyprocesowa, konfiguracja kompilacji i wdrożenie instalatora.
- Katalogi:
src/infrastructure/location/, moduł mapy (WebView/Leaflet), pliki konfiguracyjneapp.json,eas.json. - Zadania:
- Implementacja serwisu lokalizacji
ExpoLocationService.tsz pełną obsługą uprawnień czasu wykonania (Runtime Permissions). - Kontener mapy Leaflet osadzony w
WebViewwraz z dwukierunkową komunikacją przez mostek JavaScript (postMessage↔︎injectJavaScript). - Konfiguracja tożsamości aplikacji w
app.jsonoraz profilu dystrybucyjnegopreview(apk) weas.json. - Automatyzacja kompilacji instalatora w chmurze EAS Build i
dostarczenie działającego pliku
.apk.
- Implementacja serwisu lokalizacji
(W zespołach 3-osobowych obowiązki ról Data/Security oraz Hardware/DevOps zostają połączone).
Praca w Git i Rewizja Kodu (Code Review)
Cel: Wdrożenie profesjonalnego standardu pracy zespołowej eliminującego ryzyko nadpisywania kodu.
- Zasada ochrony gałęzi głównej:
- Obowiązuje bezwzględny zakaz bezpośredniego wypychania zmian
(
git push) do gałęzimain. - Prace prowadzone są na gałęziach funkcyjnych tworzonych wg wzorca:
feature/nazwa-modululubfix/opis-bledu.
- Obowiązuje bezwzględny zakaz bezpośredniego wypychania zmian
(
- Konwencja Conventional Commits:
feat:– nowa funkcjonalność (np.feat(auth): dodanie haszowania bcryptjs z expo-crypto).fix:– naprawa błędu (np.fix(storage): dodanie indeksu dla zapytań zakresowych).refactor:– refaktoryzacja kodu bez zmiany logiki biznesowej (np.refactor(domain): wydzielenie kalkulatora haversine).test:– dodanie lub modyfikacja testów (np.test(use-cases): weryfikacja filtrowania odjazdow z czasem zamrozonym).chore:– zadania konfiguracyjne i DevOps (np.chore(eas): konfiguracja profilu apk w eas.json).
- Procedura Pull Request i Code Review:
- Każde scalenie do
mainodbywa się wyłącznie poprzez Pull Request (PR). - Autor PR przygotowuje zwięzły opis wprowadzonych zmian oraz załącza zrzut ekranu lub log potwierdzający przejście testów.
- Obowiązkowe Code Review: co najmniej jeden inny członek zespołu musi przejrzeć diff zmian w kodzie, zamieścić uwagi techniczne w liniach kodu i formalnie zatwierdzić wdrożenie (Approve).
- Każde scalenie do
Warianty domenowe (Otwarte Dane Gdańsk / TRISTAR)
Cel: Realizacja praktycznego scenariusza inżynierskiego opartego o darmowe, publiczne i stabilne API.
Każdy zespół wybiera jeden z poniższych tematów:
Wariant A: UrbanPulse / Stop Scout Pro (Smart Mobility & Crowdsourcing)
- Opis: System monitorowania transportu miejskiego łączący telemetrię pojazdów live z raportowaniem incydentów terenowych (awarie, opóźnienia, tłok).
- Zasoby danych:
- TRISTAR
Pozycje GPS pojazdów na żywo (CKAN)
(Endpoint:https://ckan2.multimediagdansk.pl/gpsPositions?v=2– współrzędne, linia, kod pojazdu, opóźnienie w sekundach). - TRISTAR
Bieżące komunikaty dyspozytorskie (CKAN)
(Informacje o wstrzymaniu ruchu, objazdach i awariach). - Baza
słupków stops.json oraz odjazdy live
(
https://ckan2.multimediagdansk.pl/departures?stopId={stopId}).
- TRISTAR
Pozycje GPS pojazdów na żywo (CKAN)
- Specyfika domenowa: Filtrowanie strumienia pojazdów pod kątem widocznego prostokąta mapy oraz geofencing (blokada dodania zgłoszenia, jeśli użytkownik znajduje się dalej niż 150 m od słupka/pojazdu).
Wariant B: FieldService / StopAudit (Inspekcja i Audyt Infrastruktury Miejskiej)
- Opis: Narzędzie terenowe dla mobilnych inspektorów do ewidencji stanu wiat przystankowych, czystości, oświetlenia i tablic informacji pasażerskiej (SIP).
- Zasoby danych:
- Wykaz
obiektów infrastruktury stops.json (CKAN)
(Ponad 2500 słupków i wiat ze współrzędnymi geograficznymi i nazwami). - Rejestr komunikatów i awarii TRISTAR (CKAN)
- Katalog zbioru TRISTAR Gdańsk (CKAN)
- Wykaz
obiektów infrastruktury stops.json (CKAN)
- Specyfika domenowa: Wzorzec Outbox
Pattern w SQLite (formularze offline:
DRAFT→QUEUED→SYNCED), automatyczna podpowiedź GPS formularza najbliższego obiektu w promieniu 30 m oraz wizualne kodowanie statusu wiat na mapie.
Wariant C: EventScout / CityQuest (Grywalizacja Terenowa i Eksploracja POI)
- Opis: Aplikacja miejskiej gry eksploracyjnej łącząca nawigację komunikacją zbiorową z odkrywaniem obiektów historycznych i turystycznych Gdańska.
- Zasoby danych:
- Katalog Otwartych
Danych Gdańska (Kultura i Turystyka)
(Zbiory punktów widokowych, zabytków i pomników przyrody). - Odjazdy ZTM Live dla najbliższych przystanków:
https://ckan2.multimediagdansk.pl/departures?stopId={id}. - Komunikaty o utrudnieniach TRISTAR (CKAN).
- Katalog Otwartych
Danych Gdańska (Kultura i Turystyka)
- Specyfika domenowa: Mechanika "Mgły Wojny"
(ukrywanie szczegółów questu do momentu zbliżenia się na odległość ≤ 25
m), rejestracja punktów XP w bazie SQLite oraz radar zbliżeniowy oparty
o dynamiczne animacje
Animated.
Wymagania techniczne i wzorce architektoniczne
Cel: Implementacja pełnego stosu technologii mobilnych zgodnie z paradygmatem Czystej Architektury.
1. Czysta Architektura (Clean Architecture)
Struktura projektu musi ściśle izolować reguły biznesowe od frameworka i bibliotek zewnętrznych:
src/domain/– modele encji (entities/), kontrakty (repositories/) oraz reguły (utils/). Brak importów zreact-native,expo-sqlite,expo-location.src/use-cases/– czyste klasy przypadków użycia przyjmujące interfejsy repozytoriów przez konstruktor (Dependency Inversion).src/infrastructure/– implementacje techniczne baz danych (storage/), klientów sieciowych (api/) oraz sprzętu (location/).src/store/orazapp/– reaktywne magazyny Zustand oraz widoki Expo Routera.
// Przykład czystej reguły biznesowej w src/domain/utils/distanceCalculator.ts
export function calculateDistanceInMeters(
lat1: number,
lon1: number,
lat2: number,
lon2: number
): number {
const R = 6371e3;
const phi1 = (lat1 * Math.PI) / 180;
const phi2 = (lat2 * Math.PI) / 180;
const deltaPhi = ((lat2 - lat1) * Math.PI) / 180;
const deltaLambda = ((lon2 - lon1) * Math.PI) / 180;
const a =
Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) +
Math.cos(phi1) * Math.cos(phi2) * Math.sin(deltaLambda / 2) * Math.sin(deltaLambda / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return Math.round(R * c);
}
2. Relacyjna baza SQLite (Offline-First)
- Użycie oficjalnego modułu
expo-sqlite. - Zakaz top-level
await: Baza musi być inicjalizowana asynchronicznie w dedykowanej metodzie wewnętrznej repozytorium, aby zapobiec awarii silnika Hermes. - Optymalizacja wydajności: Włączenie trybu WAL
(
PRAGMA journal_mode = WAL;) oraz stosowanie zapytań indeksowanych z filtrowaniem zakresowym (BETWEEN minLat AND maxLat).
// Przykład bezpiecznej inicjalizacji w src/infrastructure/storage/SqliteIncidentRepository.ts
import * as SQLite from 'expo-sqlite';
import { IIncidentRepository } from '../../domain/repositories/IIncidentRepository';
import { Incident } from '../../domain/entities/Incident';
export class SqliteIncidentRepository implements IIncidentRepository {
private dbPromise: Promise<SQLite.SQLiteDatabase> | null = null;
private async getDb(): Promise<SQLite.SQLiteDatabase> {
if (!this.dbPromise) {
this.dbPromise = (async () => {
const db = await SQLite.openDatabaseAsync('urbanpulse.db');
await db.execAsync(`
PRAGMA journal_mode = WAL;
CREATE TABLE IF NOT EXISTS incidents (
id TEXT PRIMARY KEY NOT NULL,
stopId INTEGER NOT NULL,
type TEXT NOT NULL,
description TEXT NOT NULL,
createdAt TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_incidents_stopId ON incidents(stopId);
`);
return db;
})();
}
return this.dbPromise;
}
async save(incident: Incident): Promise<void> {
const db = await this.getDb();
await db.runAsync(
'INSERT INTO incidents (id, stopId, type, description, createdAt) VALUES (?, ?, ?, ?, ?)',
[incident.id, incident.stopId, incident.type, incident.description, incident.createdAt]
);
}
async getAllForStop(stopId: number): Promise<Incident[]> {
const db = await this.getDb();
return await db.getAllAsync<Incident>(
'SELECT * FROM incidents WHERE stopId = ? ORDER BY createdAt DESC',
[stopId]
);
}
}
3. Stan globalny Zustand z Optimistic UI
Zarządzanie stanem musi implementować technikę Optimistic UI – natychmiastowa modyfikacja stanu w pamięci RAM zapewnia 0 ms opóźnienia interfejsu, podczas gdy zapis do SQLite odbywa się asynchronicznie w tle. W przypadku awarii operacji dyskowej stan jest automatycznie przywracany (rollback).
// Przykład akcji Optimistic UI w src/store/useIncidentStore.ts
import { create } from 'zustand';
import { Incident } from '../domain/entities/Incident';
import { IIncidentRepository } from '../domain/repositories/IIncidentRepository';
interface IncidentState {
incidents: Incident[];
setIncidents: (items: Incident[]) => void;
addIncidentOptimistic: (incident: Incident, repo: IIncidentRepository) => Promise<boolean>;
}
export const useIncidentStore = create<IncidentState>((set, get) => ({
incidents: [],
setIncidents: (items) => set({ incidents: items }),
addIncidentOptimistic: async (incident, repo) => {
const previous = get().incidents;
set({ incidents: [incident, ...previous] }); // Natychmiastowa reakcja UI
try {
await repo.save(incident); // Asynchroniczny zapis SQLite w tle
return true;
} catch (error) {
console.error('Błąd zapisu do SQLite. Rollback stanu...', error);
set({ incidents: previous }); // Przywrócenie stanu pierwotnego
return false;
}
},
}));
4. Sensory i Mapa (expo-location + WebView)
- Pełna obsługa uprawnień czasu wykonania (Runtime Foreground Permissions).
- Obsługa mapy Leaflet/OpenStreetMap osadzonej w
WebViewz dwukierunkową komunikacją przez mostek: odbiór kliknięć przezonMessage(window.ReactNativeWebView.postMessage) oraz dynamiczne nanoszenie markerów metodąinjectJavaScript.
5. Wydajne Animacje Natywne
(Animated)
Każda animacja cykliczna lub dynamiczna (np. pulsowanie ostrzegawcze,
wskaźnik zbliżeniowy) musi bezwzględnie posiadać flagę
useNativeDriver: true. Dzięki temu kalkulacja klatek
animacji jest w całości zrzucona na procesor graficzny i wątek
renderujący systemu (UI Thread), bez blokowania logiki JavaScriptu.
6. Testy Jednostkowe w Jest
- Minimum 8 testów jednostkowych weryfikujących logikę biznesową.
- Zastosowanie atrap repozytoriów (
jest.fn()). - Wykorzystanie sztucznej symulacji upływu czasu zegara
(
jest.useFakeTimers()orazjest.setSystemTime()) do testowania reguł wygasania i kolejkowania danych.
// Przykład testu use-case'a z fałszywym zegarem w src/use-cases/__tests__/RegisterIncidentUseCase.test.ts
import { RegisterIncidentUseCase } from '../RegisterIncidentUseCase';
import { IIncidentRepository } from '../../domain/repositories/IIncidentRepository';
import { ILocationService } from '../../domain/repositories/ILocationService';
describe('RegisterIncidentUseCase', () => {
let mockRepo: jest.Mocked<IIncidentRepository>;
let mockGps: jest.Mocked<ILocationService>;
let useCase: RegisterIncidentUseCase;
beforeEach(() => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2026-10-15T12:00:00Z'));
mockRepo = {
save: jest.fn().mockResolvedValue(undefined),
getAllForStop: jest.fn(),
};
mockGps = {
requestPermissions: jest.fn().mockResolvedValue(true),
getCurrentPosition: jest.fn(),
};
useCase = new RegisterIncidentUseCase(mockRepo, mockGps);
});
afterEach(() => {
jest.useRealTimers();
});
it('odrzuca zgłoszenie incydentu, gdy dystans fizyczny przekracza 150m', async () => {
mockGps.getCurrentPosition.mockResolvedValue({
latitude: 54.371,
longitude: 18.618,
});
await expect(
useCase.execute({
stopId: 2072,
stopCoords: { latitude: 54.352, longitude: 18.646 },
type: 'DELAY',
description: 'Blokada toru',
})
).rejects.toThrow('Zbyt duża odległość od obiektu (wymagane <= 150m).');
expect(mockRepo.save).not.toHaveBeenCalled();
});
});
7. Samodzielny instalator APK (EAS Build)
- Konfiguracja profilu
previeww plikueas.jsonz parametrem"buildType": "apk". - Kompilacja instalatora w chmurze Expo
(
eas build -p android --profile preview) lub lokalnie (--local). - Wygenerowanie instalatora binarnego
.apk, który działa na telefonie w sposób autonomiczny – bez konieczności uruchamiania serwera na komputerze i bez aplikacji Expo Go.
Harmonogram odbiorów i punkty kontrolne (Milestones)
Przedmiot obejmuje 8 godzin lekcyjnych podzielonych na dwa bloki po 4 godziny oraz asynchroniczny kamień milowy weryfikowany w repozytorium GitHub / platformie Moodle:
| Etap | Termin rozliczenia | Weryfikowany zakres prac | Punkty |
|---|---|---|---|
| Spotkanie 1 (Laboratorium 1) |
Podczas 1. bloku
zajęć (4 godziny lekcyjne) |
Kick-off projektu i fundamenty
architektoniczne: • Podział zespołu na role inżynierskie i wybór wariantu domenowego (A, B lub C). • Inicjalizacja repozytorium Git, konfiguracja reguł ochrony gałęzi, Conventional Commits i szablonów PR. • Implementacja szkieletu Clean Architecture ( domain/, use-cases/,
infrastructure/). |
15 pkt |
| Kamień
Milowy (Asynchronicznie) |
Przed Spotkaniem
2 (Rozliczenie na Moodle/Git) |
Implementacja modułów, testy i
kompilacja APK: • Baza expo-sqlite (indeksy,
Bounding Box, brak top-level await) i haszowanie haseł
bcryptjs.• Integracja REST API (CKAN/TRISTAR) i globalny magazyn Zustand (Optimistic UI z rollbackiem). • Moduł GPS expo-location, mapa kafelkowa Leaflet w WebView i animacje
Animated (useNativeDriver).• Minimum 8 testów jednostkowych w Jest (fałszywy zegar, mocki repozytoriów). • Samodzielny plik instalacyjny .apk wygenerowany przez EAS
Build. |
45 pkt |
| Spotkanie 2 (Laboratorium 2) |
Podczas 2. bloku
zajęć (4 godziny lekcyjne) |
Finałowy odbiór, audyt inżynierski
i weryfikacja kodu: • Prezentacja działania samodzielnego pliku .apk na telefonie (narzędzie
scrcpy), test trybu offline (samolotowego).• Audyt repozytorium Git: wkład autorów, jakość i merytoryczność dyskusji w Code Review. • Indywidualna weryfikacja znajomości kodu każdego członka zespołu (z procedurą ratunkową). |
40 pkt |
| SUMA | 100 pkt |
Procedura odbioru końcowego i weryfikacja znajomości kodu (Spotkanie 2)
Spotkanie 2 jest w całości przeznaczone na weryfikację projektów, obronę oraz wystawienie ocen w protokole. Każdy zespół przystępuje do 20-minutowego odbioru na stanowisku laboratoryjnym:
1. Prezentacja aplikacji Standalone APK (5 minut)
- Prezentacja działania aplikacji zainstalowanej z wygenerowanego
pliku
.apkna fizycznym telefonie, sklonowanym na ekran za pomocą programu scrcpy. - Serwer Metro Bundler na komputerze musi być zatrzymany
(
Ctrl + C) – aplikacja działa w 100% autonomicznie. - Test trybu samolotowego (Offline-First): Prowadzący odłącza telefon od sieci komórkowej i Wi-Fi. Aplikacja musi zachować pełną dostępność danych z bazy SQLite oraz wykonywać operacje Optimistic UI bez zgłaszania błędów krytycznych.
2. Audyt repozytorium Git i architektury (5 minut)
- Weryfikacja historii commitów: sprawdzenie zakazu bezpośrednich
pushów do
mainoraz poprawności formatu Conventional Commits. - Przegląd Pull Requestów: ocena merytoryczna uwag zgłoszonych w ramach Code Review przez poszczególnych autorów.
- Weryfikacja czystości warstw Clean Architecture i kompletności
typowania TypeScript (brak typów
any).
3. Indywidualna weryfikacja znajomości kodu (10 minut)
Prowadzący przeprowadza szczegółowe odpytanie każdego członka zespołu z zakresu kodu, za który odpowiadał w ramach przypisanej roli. Weryfikowana jest znajomość przepływu danych, zrozumienie wzorców projektowych, obsługa stanów brzegowych oraz mechanika integracji komponentów.
⚠️ Procedura warunkowa (Zadanie ratunkowe w przypadku braku zrozumienia kodu)
W przypadku, gdy student wykaże rażący brak zrozumienia kodu, pod którym się podpisał (nie potrafi wyjaśnić działania funkcji, przepływu stanu lub zapytań SQL), nie otrzymuje natychmiastowej oceny niedostatecznej. Student ma możliwość obronienia oceny poprzez natychmiastowe doimplementowanie na żywo nowej mikrofunkcjonalności przed prowadzącym, odpowiadającej przypisanej mu roli w zespole:
- Dla Inżyniera Domeny & Jakości (Domain & QA
Lead):
- Zadanie ratunkowe: Zaimplementowanie w warstwie domeny
nowej, czystej reguły biznesowej/walidacyjnej (np. kalkulator prędkości
przemieszczania się lub klasyfikator priorytetów incydentów) oraz
dopisanie do niej testu jednostkowego w Jest uwzględniającego wartości
brzegowe (
null, wartości ujemne, formaty niestandardowe).
- Zadanie ratunkowe: Zaimplementowanie w warstwie domeny
nowej, czystej reguły biznesowej/walidacyjnej (np. kalkulator prędkości
przemieszczania się lub klasyfikator priorytetów incydentów) oraz
dopisanie do niej testu jednostkowego w Jest uwzględniającego wartości
brzegowe (
- Dla Inżyniera Danych & Bezpieczeństwa (Data &
Security Lead):
- Zadanie ratunkowe: Rozszerzenie schematu relacyjnego w
expo-sqliteo nową tabelę powiązaną relacją 1:N (np. tagi incydentu lub historia statusów zgłoszenia) oraz dopisanie zoptymalizowanych metod zapisu (INSERT) i odczytu (SELECT) w repozytorium infrastruktury.
- Zadanie ratunkowe: Rozszerzenie schematu relacyjnego w
- Dla Inżyniera UI/UX & Reaktywności (UI & State
Lead):
- Zadanie ratunkowe: Zdefiniowanie w magazynie Zustand nowej akcji w trybie Optimistic UI (np. licznik polubień/potwierdzeń zgłoszenia przez innych użytkowników z obsługą rollbacku) oraz podpięcie jej pod dedykowany przycisk na karcie widoku.
- Dla Inżyniera Integracji Natywnej & DevOps (Hardware
& DevOps Lead):
- Zadanie ratunkowe: Rozszerzenie komunikacji przez mostek
WebView↔︎ React Native o obsługę nowego typu zdarzeniapostMessage(np. kliknięcie w dowolny punkt mapy przekazuje koordynaty i centruje widok) lub dodanie nowego wariantu animacji z modułuAnimated(np. cykliczna zmiana skaliscale) z zachowaniem flagiuseNativeDriver: true.
- Zadanie ratunkowe: Rozszerzenie komunikacji przez mostek
Kryteria oceny projektu (100 pkt)
| Obszar oceny | Weryfikowany zakres techniczny | Punkty |
|---|---|---|
| 1. Architektura & TypeScript | Ścisłe przestrzeganie Clean Architecture
(brak importów UI/bazy w domenie), dependency inversion w use-cases,
pełne typowanie TypeScript (brak any). |
20 pkt |
| 2. Baza SQLite & Offline-First | Brak blokującego top-level await, optymalny schemat (indeksy, tryb WAL), zapytania przestrzenne (Bounding Box), bezbłędne działanie aplikacji w trybie samolotowym. | 15 pkt |
| 3. Stan Globalny & Optimistic UI | Właściwe wykorzystanie Zustand, implementacja Optimistic UI (natychmiastowy update w RAM) oraz poprawny mechanizm rollbacku stanu w razie awarii zapisu do SQLite. | 15 pkt |
| 4. Sensory, Mapa & Animacje | Obsługa cyklu życia uprawnień w
expo-location, wydajna mapa w WebView z mostkiem
postMessage, animacje modułu Animated z
useNativeDriver: true. |
15 pkt |
| 5. Jakość, Testy & Wdrożenie APK | Minimum 8 testów jednostkowych Jest
(domena, zamockowany use-case z fałszywym zegarem), poprawny profil w
eas.json, samodzielny instalator .apk
działający bez Metro. |
15 pkt |
| 6. Kultura Inżynierska Git | Brak pushów do maina, gałęzie funkcyjne, Conventional Commits, merytoryczne dyskusje i recenzje w Code Review (PR) z aktywnym udziałem każdego członka zespołu. | 10 pkt |
| 7. Znajomość Kodu & Obrona | Płynne wyjaśnienie przepływu danych i użytych wzorców podczas odpytania (lub pomyślne wykonanie zadania ratunkowego na żywo na stanowisku). | 10 pkt |
| SUMA | 100 pkt |