---
alwaysApply: true
title: Vibeviewer Architecture Guidelines 
---

## Background

- The project uses a layered, modular Swift Package architecture with goals: minimal public surface, one-way dependencies, single responsibility, testability, and replaceability.
- Layers and dependency direction (top-down only):
  - Core/Shared → common utilities and extensions (no business-layer dependencies)
  - Model → pure data/DTO/domain entities (may depend on Core)
  - API/Service → networking/IO/3rd-party orchestration and DTO→domain mapping (depends on Model + 3rd-party)
  - Feature/UI → SwiftUI views and interactions (depends on API-exposed service protocols and domain models; must not depend on networking libraries)
- Architectural style: Native SwiftUI MV (not MVVM). State via @State/@Observable; dependency injection via @Environment; concurrency with async/await and @MainActor.

## Do (Recommended)

- Module placement & responsibilities
  - Before adding code, decide whether it belongs to UI/Service/Model/Core and place it in the corresponding package/directory; one type/responsibility per file.
  - The API layer exposes only “service protocol + default implementation”; networking library/targets/plugins are encapsulated internally.
  - Service functions return domain models (Model-layer entities) or clear error types; avoid leaking DTOs to the UI.

- Domain models & mapping
  - Abstract API response DTOs into domain entities (e.g., UserProfile / UsageOverview / TeamSpendOverview / UsageEvent / FilteredUsageHistory).
  - Perform DTO→domain mapping in the API layer; UI consumes domain-only.

- Dependencies & visibility
  - One-way: Core ← Model ← API ← Feature.
  - Default to internal; use public only for cross-package use; prefer protocols over concrete types.

- SwiftUI & concurrency
  - Inject services via @Environment; place side effects in .task / .onChange so they automatically cancel with the view lifecycle.
  - UI updates occur on @MainActor; networking/IO on background using async/await; cross-isolation types must be Sendable.

- Testing & replaceability
  - Provide an injectable network client interface for services; separate default implementation from testable construction paths.
  - Put utilities/algorithms into Core; prefer pure functions for unit testing and reuse.

- Troble Shooting
 - if you facing an lint error by "can't not found xxx in scope" when you edit/new/delete some interface on Package, that means you need to call XCodeBuildMCP to rebuild that package, so that other package can update the codebase to fix that error

## Don't (Avoid)

- UI directly depending on networking libraries, triggering requests, or being exposed to backend error details.
- Feature depending on API internals (e.g., Targets/Plugins/concrete networking implementations).
- Exposing API DTOs directly to the UI (causes global coupling and fragility).
- Reverse dependencies (e.g., Model depends on Feature; API depends on UI).
- Introducing MVVM/ViewModel as the default; or using Task { } in onAppear (use .task instead).
- Overusing public types/initializers; placing multiple unrelated types in one file.

## Review checklist

1) Quadrant self-check (placement)
   - UI/interaction/rendering → Feature/UI
   - Networking/disk/auth/3rd-party → API/Service
   - Pure data/DTO/state aggregation → Model
   - Utilities/extensions/algorithms → Core

2) Surface area & replaceability
   - Can it be exposed via protocol to hide details? Is internal sufficient by default?
   - Do services return only domain models/error enums? Is it easy to replace/mock?

3) Dependency direction & coupling
   - Any violation of Core ← Model ← API ← Feature one-way dependency?
   - Does the UI still reference DTOs or networking implementations? If yes, move mapping/abstraction to the API layer.

4) Concurrency & thread safety
   - Are UI updates on @MainActor? Are cross-isolation types Sendable? Are we using async/await?
   - Should serialization-required persistence/cache be placed within an Actor boundary?

5) File organization & naming
   - Clear directories (Feature/Views, API/Service, API/Targets, API/Plugins, Model/Entities, Core/Extensions).
   - One type per file; names reflect layer and responsibility (e.g., FeatureXView, FeatureXService, GetYAPI, ZResponse).
   - Package directory structure: Sources/<PackageName>/ organized by feature subfolders; avoid dumping all source at one level.
     - Suggested subfolders:
       - API: Service / Targets / Plugins / Mapping (DTO→Domain mapping)
       - Feature: Views / Components / Scenes / Modifiers
       - Model: Entities
       - Core: Extensions / Utils
   - Consistent naming: use a shared prefix/suffix for similar features for discoverability.
     - Suffix examples: …Service, …API, …Response, …Request, …View, …Section, …Window, …Plugin, …Mapper.
     - Use a consistent domain/vendor prefix where needed (e.g., Cursor…).
   - File name equals type name: each file contains only one primary type; exact case-sensitive match.
     - Protocol/implementation convention: protocol uses FooService; default implementation uses DefaultFooService (or LiveFooService). Expose only protocols and inject implementations.

   - Model-layer naming (Entities vs DTOs):
     - Entities (exposed to business/UI):
       - Use domain-oriented neutral nouns; avoid vendor prefixes by default (e.g., UserProfile, UsageOverview, TeamSpendOverview, UsageEvent, FilteredUsageHistory, AppSettings, Credentials, DashboardSnapshot).
       - If source domain must be shown (e.g., “Cursor”), use a consistent prefix within that domain (e.g., CursorCredentials, CursorDashboardSnapshot) for consistency and discoverability.
       - Suggested suffixes: …Overview, …Snapshot, …History, …Event, …Member, …RoleCount.
       - Prefer struct, value semantics, and Sendable; expose public types/members only when needed cross-package.
       - File name equals type name; single-type files.
     - DTOs (API layer only, under API/Mapping/DTOs):
       - Use vendor/source prefix + semantic suffix: e.g., Cursor…Request, Cursor…Response, Cursor…Event.
       - Default visibility is internal; do not expose to Feature/UI; map to domain in the API layer only.
       - File name equals type name; single-type files; field names mirror backend responses (literal), adapted to domain naming via mapping.
       - Mapping lives in the API layer (Service/Mapping); UI/Feature must never depend on DTOs.

## Pre-PR checks
   - Remove unnecessary public modifiers; check for reverse dependencies across layers.
   - Ensure UI injects services via @Environment and contains no networking details.
   - Ensure DTO→domain mapping is complete, robust, and testable.

Note: When using iOS 26 features, follow availability checks and progressive enhancement; ensure reasonable fallbacks for older OS versions.

## FAQ
 - After adding/removing module code, if lint reports a missing class but you are sure it exists, rebuild the package with XcodeBuild MCP and try again.

