Decyzja: warstwa logiczna ma dwie wymienne implementacje silnika obliczeń — własną/permisywną (ścieżka A) i AGPL (Swiss Ephemeris) — dla wbudowanego frameworku porównań i walidacji. Prezentacja i dane pozostają wspólne. - docs/architektura-dwoch-silnikow.md: schemat, izolacja licencyjna silnika AGPL jako osobnej usługi (engine-swisseph), mechanizm dual-run, mapowanie na wymagania, profile wdrożeniowe. - astrololo_wymagania.xlsx (Warstwa logiczna): rewizja LOG-24 (EngineProvider z dwoma backendami) i LOG-25 (framework porównawczy); nowe LOG-26 (dual-run + raport różnic), LOG-27 (izolacja licencyjna silnika AGPL), LOG-28 (kontrakt parzystości silników). Licznik w Przeglądzie: 28. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.7 KiB
Architektura dwóch silników (warstwa logiczna)
Dokument roboczy. Decyzja: warstwa logiczna ma dwie wymienne implementacje silnika obliczeń — jedną własną/permisywną (ścieżka A) i jedną opartą o AGPL (ścieżka C, Swiss Ephemeris) — żeby od pierwszego dnia mieć wbudowany framework porównań i walidacji. Warstwy prezentacji i danych pozostają wspólne i pojedyncze; rozdwaja się wyłącznie silnik wewnątrz warstwy logicznej.
1. Schemat
(jedna) (jedna, ale 2 silniki) (jedna)
┌──────────────────────┐ ┌─────────────────────────────────────┐ ┌──────────────────────┐
│ PREZENTACJA │ ─────▶ │ LOGIKA │ ─────▶ │ DANE │
│ (bez zmian) │ ◀───── │ EngineProvider (interfejs) │ ◀───── │ (bez zmian) │
└──────────────────────┘ │ ├── EngineA: własny (permisywny)│ └──────────────────────┘
│ │ Skyfield/Moshier — in-proc │
│ └── EngineB: swisseph (AGPL) │
│ ⇢ OSOBNY proces/usługa │
│ Komparator (dual-run + raport) │
└─────────────────────────────────────┘
│
⇢ (granica sieciowa/IPC)
┌─────────────────────────────────┐
│ engine-swisseph (AGPL, OSOBNO) │
│ pyswisseph / Swiss Ephemeris │
└─────────────────────────────────┘
Prezentacja i dane „widzą" tylko interfejs EngineProvider — nie wiedzą, który silnik liczy (engine‑agnostic). To ta sama zasada modułowości co DataProvider w warstwie danych.
2. Po co dwa silniki
- Wyrocznia walidacyjna od dnia zero. Każdy wynik EngineA możemy natychmiast porównać z EngineB (Swiss Ephemeris = de‑facto standard). Bezcenne przy budowie własnego silnika od zera (primary directions, domy, węzły/Lilith).
- Framework porównań. Tryb „policz oboma i pokaż różnice" — do testów regresyjnych (CI) i do podglądu side‑by‑side w UI.
- Bezpieczeństwo decyzji. Gdyby własny silnik gdzieś odstawał, widać to od razu, a nie po miesiącach.
3. Izolacja licencyjna (warunek konieczny) ⚠️
EngineB jest AGPL, więc nie może być wlinkowany w permisywny produkt. Rozwiązanie zgodne z naszą architekturą (osobne usługi jak warstwa danych):
- EngineB = osobny proces/usługa
engine-swisseph, wołany przez granicę sieciową/IPC (HTTP). Brak linkowania = brak „zarażenia" copyleftem reszty. - Produkt zamknięty (dystrybucja/hosting): prezentacja + logika(EngineA) + dane. Bez EngineB. Zero AGPL.
- Profil porównawczy/dev/CI: dodatkowo uruchamiamy usługę
engine-swisseph(AGPL, sama w sobie zgodna z AGPL — to w zasadzie cienkie API na pyswisseph). Używana wewnętrznie do porównań, nie wystawiana użytkownikom produktu. - Konfiguracja decyduje, czy EngineB jest w ogóle dostępny (
ENGINE_B_URLustawione lub nie).
Efekt: pełny framework porównań bez narażania kodu i danych produktu na obowiązki AGPL (patrz przeglad-bibliotek-i-licencji.md oraz analiza zakresu AGPL względem baz danych).
4. Jak działa porównanie (dual‑run)
- To samo wejście (dane horoskopu + ustawienia) trafia do EngineA i EngineB.
- Komparator zestawia wyniki: pozycje obiektów, cusps domów, aspekty, daty technik.
- Różnice liczone z progami tolerancji per wielkość (np. długość ekliptyczna ≤ 1″, cusp ≤ 1″, data zdarzenia ≤ 1 min); przekroczenia oflagowane.
- Raport: (a) jako asercje w CI (regresja), (b) jako podgląd side‑by‑side w prezentacji na żądanie.
5. Wpływ na warstwy i mapowanie na wymagania
| Warstwa | Zmiana |
|---|---|
| Prezentacja | brak (opcjonalnie: widok porównawczy side‑by‑side) |
| Dane | brak |
| Logika | EngineProvider + 2 backendy (EngineA in‑proc, EngineB jako klient usługi AGPL) + komparator |
| Nowa usługa | engine-swisseph (AGPL, osobno, opcjonalna) |
Wymagania (arkusz „Warstwa logiczna"):
- LOG‑24 — interfejs
EngineProviderz dwoma backendami (zrewidowane). - LOG‑25 — framework walidacyjno‑porównawczy (zrewidowane).
- LOG‑26 — tryb dual‑run + raport różnic (nowe).
- LOG‑27 — izolacja licencyjna silnika AGPL jako osobnej usługi (nowe).
- LOG‑28 — kontrakt parzystości silników: identyczny interfejs, te same testy (nowe).
6. Implikacje wdrożeniowe
- Dwa profile
docker-compose:proprietary(prezentacja+logika+dane, EngineA) icomparison(dodajeengine-swisseph). - Testy kontraktowe uruchamiane na obu silnikach (ten sam zestaw → gwarancja parzystości interfejsu).
- Decyzja A vs B na produkcji pozostaje po stronie A; B nigdy nie trafia do dystrybucji zamkniętej — służy jako wzorzec i narzędzie QA.