Skip to content

Latest commit

Β 

History

History
88 lines (66 loc) Β· 6.32 KB

File metadata and controls

88 lines (66 loc) Β· 6.32 KB

ARCHITECTURE.md

High-level system design for BrewUI. Update when structure changes. Owns: layers, data flow, integrations (Homebrew CLI / JSON API), and product constraints. Defers naming and day-to-day coding patterns to CONVENTIONS.md. Record durable rationale in .ai/memory.md.

Tech Stack

Concern Choice
Language Swift 6.0 (strict concurrency mode)
UI Framework SwiftUI β€” pure; use AppKit only when SwiftUI cannot meet a requirement
State management @Observable (Swift 5.9+)
Concurrency async/await throughout; actor isolation for shared mutable state
Package manager Swift Package Manager
macOS targets Tahoe 26, Sequoia 15, Sonoma 14 β€” minimum: Tahoe 26
Data sources Homebrew JSON API (formulae.brew.sh) + brew CLI subprocess
Deployment Unsandboxed macOS app (default Homebrew prefix)

System shape

Flow: View β†’ ViewModel β†’ Repository or Interactor β†’ Services β†’ brew CLI or JSON API.

Guiding patterns:

  • MVVM-C (lightweight): Views stay declarative; ViewModels own presentation state; coordination/navigation policy is centralized in small coordinator-style shell types when needed.
  • Clean Architecture principles: Depend inward on abstractions, keep use cases in Interactors/Repositories, isolate infrastructure in Services, and keep UI/framework concerns out of domain decisions.
  • Emergent architecture: Prefer the smallest pattern that solves today’s problem; evolve structure incrementally as features and complexity grow.
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    BrewUI (macOS App)                        β”‚
β”‚  Views (SwiftUI) ──▢ ViewModels                              β”‚
β”‚         β”‚                    β”‚                               β”‚
β”‚         β”‚          Repositories    Interactors               β”‚
β”‚         β”‚                 β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                        β”‚
β”‚         β”‚                      Services (brew + JSON API)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β–Ό
              brew CLI (subprocess) Β· formulae.brew.sh JSON API

Core components

  • Views: SwiftUI; thin β€” bind state, forward actions.
  • Feature root views (*Root): Composition boundaries that bridge app-level dependencies into feature content. Root wrappers read environment-level dependencies and construct/inject content-view dependencies while managing view-model lifecycle boundaries.
  • ViewModels: Presentation state and mapping; delegate work downward. Keep domain rules in Interactors or Models, not here.
  • Presentation boundary: Domain models remain UI-agnostic. Map domain values to UI-ready properties through feature ViewModels for top-level surfaces, or dedicated feature Item types for subview/action-level presentation needs.
  • Repositories: CRUD-shaped access to a data source (CLI output, API, storage, in-memory). Swap the implementation, keep the contract.
  • Interactors: One use case each β€” not generic CRUD (e.g. a doctor run). Prefer protocols + real/mock impls for tests. (One-shot reads such as the brew config snapshot are modelled as Repositories here, not Interactors.)
  • Services: Infrastructure grouped by integration boundaries.
  • Command center: BrewCommandCenter (actor protocol; app default SerialBrewCommandCenter) β€” serializes mutating brew work, tracks in-flight / failed operation state (BrewOperationID + BrewOperationPhase) for UI across surfaces, and runs small BrewMutatingCommand types that call BrewCommandRunning + the brew locator. It does not own read/parsing of brew list / brew info output β€” that stays in repositories. Feature-scoped executors (e.g. upgrade helpers) should stay thin and be invoked from commands the center schedules, not as a second parallel pipeline.
  • Models: Domain-only value types and relationships shared across layers. Keep UI/presentation helpers, transport decoding models, and infrastructure-state containers out of domain models.

Command execution

Run Homebrew commands asynchronously via subprocess; support cancellation; stream or preserve stdout/stderr for transparency and logs. Always make the exact command visible to the user; treat CLI text output as unstable (tolerant parsing, fallbacks).

JSON API

Use the Homebrew JSON API where it helps. Prefer optional / resilient decoding β€” schema can change; never crash on unknown fields. Combine with CLI only as needed when the app grows.

Constraints & decisions

  • macOS-only. No iOS / iPadOS / cross-platform for now.
  • Default Homebrew prefix only: /opt/homebrew (Apple Silicon) or /usr/local (Intel). No custom prefix initially.
  • No custom taps initially β€” core tap scope.
  • brew is the source of truth β€” BrewUI does not poke Homebrew internals.
  • Transparency β€” users see what runs; no hidden commands.
  • Homebrew is separate β€” detect and degrade if missing; do not bundle Homebrew.
  • Detection: try /opt/homebrew/bin/brew then /usr/local/bin/brew.
  • Open source β€” patterns should stay contributor-friendly.

Platform constraints

  • CLI drift: brew text is not a stable API β€” parsers must be tolerant.
  • JSON drift: decoding must stay resilient as the API evolves.
  • UI tests: async output and sheets need deliberate sync; avoid flakiness.
  • Accessibility: desktop workflows need keyboard and VoiceOver semantics, not only labels.

Resources

Updating this file

Change when layers or major assumptions shift. Put why in .ai/memory.md when it is non-obvious or contentious.