Files
astrololo/docs/architektura-dwoch-silnikow.md
gitea 21442b35b9 Wymagania: dwa silniki w warstwie logicznej (własny + AGPL) do porównań
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>
2026-06-27 16:50:56 +02:00

5.7 KiB
Raw Permalink Blame History

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 EngineProvidernie wiedzą, który silnik liczy (engineagnostic). To ta sama zasada modułowości co DataProvider w warstwie danych.


2. Po co dwa silniki

  1. Wyrocznia walidacyjna od dnia zero. Każdy wynik EngineA możemy natychmiast porównać z EngineB (Swiss Ephemeris = defacto standard). Bezcenne przy budowie własnego silnika od zera (primary directions, domy, węzły/Lilith).
  2. Framework porównań. Tryb „policz oboma i pokaż różnice" — do testów regresyjnych (CI) i do podglądu sidebyside w UI.
  3. 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_URL ustawione 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 (dualrun)

  1. To samo wejście (dane horoskopu + ustawienia) trafia do EngineA i EngineB.
  2. Komparator zestawia wyniki: pozycje obiektów, cusps domów, aspekty, daty technik.
  3. Różnice liczone z progami tolerancji per wielkość (np. długość ekliptyczna ≤ 1″, cusp ≤ 1″, data zdarzenia ≤ 1 min); przekroczenia oflagowane.
  4. Raport: (a) jako asercje w CI (regresja), (b) jako podgląd sidebyside w prezentacji na żądanie.

5. Wpływ na warstwy i mapowanie na wymagania

Warstwa Zmiana
Prezentacja brak (opcjonalnie: widok porównawczy sidebyside)
Dane brak
Logika EngineProvider + 2 backendy (EngineA inproc, EngineB jako klient usługi AGPL) + komparator
Nowa usługa engine-swisseph (AGPL, osobno, opcjonalna)

Wymagania (arkusz „Warstwa logiczna"):

  • LOG24 — interfejs EngineProvider z dwoma backendami (zrewidowane).
  • LOG25 — framework walidacyjnoporównawczy (zrewidowane).
  • LOG26 — tryb dualrun + raport różnic (nowe).
  • LOG27 — izolacja licencyjna silnika AGPL jako osobnej usługi (nowe).
  • LOG28 — kontrakt parzystości silników: identyczny interfejs, te same testy (nowe).

6. Implikacje wdrożeniowe

  • Dwa profile docker-compose: proprietary (prezentacja+logika+dane, EngineA) i comparison (dodaje engine-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.