From 125eda76e85bd18651165241db8ec3153c0b93e8 Mon Sep 17 00:00:00 2001 From: franzap <_@franzap.com> Date: Mon, 19 Jan 2026 22:59:08 -0300 Subject: [PATCH] Architecture improvements --- CONTEXT.md | 9 ---- spec/guidelines/ARCHITECTURE.md | 83 +++++++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 40225ad..8a92276 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -53,18 +53,9 @@ If a guideline seems wrong or incomplete, report it as a Spec Issue. | `test/**` | Shared | Yes | | `CONTEXT.md` | Human | No | -## Key Dependencies - -- **models** / **purplebase**: Nostr SDK (local-first storage, relay sync, domain models). - See package README in pub cache. Basic usage patterns in `spec/guidelines/ARCHITECTURE.md`. -- **amber_signer**: NIP-55 Android signer integration. -- **background_downloader**: Download management with pause/resume. - ## Working Rules - Prefer small, localized changes. Avoid unrelated refactors. - After dependency changes, run: `fvm flutter pub get` - Fix any analyze/lint errors introduced by your changes. -- Do not use polling or artificial `Future.delayed`; await Futures/Streams correctly. -- Keep `lib/widgets/common` generic and reusable. - Assume Android as default target unless instructed otherwise. diff --git a/spec/guidelines/ARCHITECTURE.md b/spec/guidelines/ARCHITECTURE.md index 2069c64..5af9916 100644 --- a/spec/guidelines/ARCHITECTURE.md +++ b/spec/guidelines/ARCHITECTURE.md @@ -1,35 +1,98 @@ # Zapstore — Architecture ## Core Principle + Architecture exists to prevent accidental coupling and hidden ownership. -Each package has clear responsibilities and must not exceed them. +Each layer has clear responsibilities and must not exceed them. -## Package Responsibilities +## Layers + +### zapstore (this Flutter app) + +- Flutter UI and application orchestration +- Navigation, presentation, and user interaction +- Coordinates use cases across dependencies +- Must not contain domain rules or persistence logic + +### Dart dependencies + +#### models -### models - Domain models for Nostr events, kinds, zaps, releases, and NWC - Parsing, validation, signing, encryption, and verification - Pure domain logic only - Must not depend on storage, networking, isolates, or UI -### purplebase +#### purplebase + - Local-first storage and indexing (SQLite) - Relay synchronization and subscription lifecycle management - Background work and isolate execution - Must not depend on UI or presentation logic -### zapstore -- Flutter UI and application orchestration -- Navigation, presentation, and user interaction -- Coordinates use cases across packages -- Must not contain domain rules or persistence logic - ## Dependency Rules + - zapstore → purplebase → models - Reverse dependencies are forbidden - UI widgets must not manage relay connections, storage, or background jobs ## Ownership & Orchestration + - Relay pools and subscriptions are owned by purplebase - Background work lifecycle is explicit and cancellable - zapstore orchestrates flows but does not own low-level resources + +## Common Patterns + +### Widget watching data + +```dart +// Watch a query provider — reactive, auto-disposes +final state = ref.watch( + query( + authors: {pubkey}, + source: const LocalAndRemoteSource(relays: {'social'}), + ), +); + +return switch (state) { + StorageLoading() => CircularProgressIndicator(), + StorageError(:final exception) => Text('Error: $exception'), + StorageData(:final models) => ProfileWidget(models.first), +}; + +// Nested queries with `and` — loads relationships +final appState = ref.watch( + query( + tags: {'#d': {identifier}}, + and: (app) => { + app.latestRelease.query( + source: const LocalAndRemoteSource(relays: 'AppCatalog', stream: false), + and: (release) => {release.latestMetadata.query()}, + ), + }, + source: const LocalAndRemoteSource(relays: 'AppCatalog'), + subscriptionPrefix: 'app-detail', + ), +); +``` + +### Imperative queries (notifiers/services) + +```dart +// One-shot query via storage extension +final apps = await ref.storage.query( + RequestFilter(authors: {pubkey}, limit: 20).toRequest(), +); +``` + +### Saving and publishing (from callbacks) + +```dart +onPressed: () async { + await ref.storage.save({signedModel}); + await ref.storage.publish({signedModel}); +} +``` + +For detailed API, see models/purplebase READMEs in pub cache.