Szkielet aplikacji trójwarstwowej (prezentacja / logika / dane)

Trzy niezależne usługi FastAPI komunikujące się przez HTTP/JSON, każda
zna tylko adres warstwy bezpośrednio pod nią:

- presentation (:8000) — strona WWW + formularz
- logic (:8001) — reguły biznesowe, pośrednik
- data (:8002) — wyszukiwanie danych za interfejsem DataProvider

Warstwa danych: czytanie setek plików .xlsx z wykrywaniem nagłówka i
mapowaniem układu kolumn na schemat kanoniczny, z 4-poziomowym cache
(schemat L1, Parquet L2, wyniki zapytań L3, odwrócony indeks L4) i
unieważnianiem po odcisku pliku. Gotowa ścieżka migracji do SQL
(ingest/to_sql.py + SqlDataProvider, przełączane przez DATA_PROVIDER).

Zawiera docker-compose, Makefile, generator danych przykładowych.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 17:35:09 +02:00
parent a43d1ff955
commit 16d35c16dc
52 changed files with 1491 additions and 1 deletions
+46
View File
@@ -0,0 +1,46 @@
# Warstwa bazodanowa (`data`)
Niezależna usługa. **Jedyne zadanie:** wyszukać dane i podać je w górę. Nie zna
warstwy logicznej ani prezentacji — komunikacja wyłącznie przez HTTP/JSON
(`models.py`).
## API
- `POST /search``SearchQuery``SearchResult`
- `GET /health``HealthInfo`
## Architektura wewnętrzna
```
providers/ wymienna implementacja (wzorzec Repository)
base.py interfejs DataProvider ← kontrakt
excel_provider dziś: Excel + 4 poziomy cache
sql_provider jutro: SQL (ten sam interfejs)
factory.py DATA_PROVIDER=excel|sql
excel/ wykrywanie nagłówka + mapowanie układu kolumn (jedyne miejsce znające .xlsx)
cache/ fingerprint, schema(L1), frame/parquet(L2), query(L3), index(L4)
ingest/ build_index.py (warmup), to_sql.py (migracja ETL)
```
## Cache — dlaczego szybko
| Poziom | Co cache'uje | Zysk |
|-------|---------------|------|
| L1 `schema.db` | wykryty nagłówek + układ kolumn per plik | brak ponownego skanu heurystyką |
| L2 Parquet | znormalizowany arkusz | 10100× szybciej niż parsowanie `.xlsx` |
| L3 `QueryCache` | wynik zapytania (TTL/LRU) | powtarzalne zapytania natychmiast |
| L4 `index.db` | odwrócony indeks wartość→plik | otwieramy tylko trafione pliki, nie setki |
Unieważnianie: klucz = odcisk pliku (`mtime+rozmiar`, opcjonalnie `sha256`).
Zmiana pliku → inny odcisk → automatyczny przebudowa.
## Uruchomienie lokalne
```bash
pip install -r requirements.txt
python scripts/make_sample_data.py # przykładowe .xlsx
python -m app.ingest.build_index # (opcjonalnie) prebuild indeksu
uvicorn app.main:app --port 8002
```
## Migracja do SQL (gdy nadejdzie czas)
```bash
python -m app.ingest.to_sql # Excel -> tabela 'records' + indeksy
export DATA_PROVIDER=sql # przełącz warstwę — reszta systemu bez zmian
```