SkillAgentSearch skills...

architecture-fsd

Feature-Sliced Design — layers, import rules, and slice structure for src/renderer

Install / Use

npx skills add AK2083/DojoSphere

Installs into whichever agent you are using.

About this skill
🔧

.clinerules

Cline rules

Quality Score

64/100

Supported Platforms

Cline

description: Feature-Sliced Design — layers, import rules, and slice structure for src/renderer globs: src/renderer/**/* alwaysApply: false

Feature-Sliced Design (FSD)

Renderer code lives under src/renderer/ in five layers. Imports may only point downward — never from shared toward features, pages, widgets, or app.

app → pages, widgets, features, shared
pages → features, widgets, shared
widgets → features, shared
features → shared (+ slices of the same feature among themselves)
shared → internal only

app vs. shared — decision guide

Classify new dependencies by role, not technology:

| Question | → app | → shared | | ----------------------------------------------------------------------- | ------- | ---------- | | Part of the composition root (start app, register plugins)? | Yes | No | | Swappable/mockable without rewriting features? | No | Yes | | Features should not import SDKs directly? | — | Yes | | Switching mainly requires wiring changes in main.ts/providers/? | Yes | No |

Rules of thumb:

  • Render framework and global providers (app shell) → app/providers/ — configure directly, do not abstract.
  • Vendor/infrastructure clients (backend, storage bridge, …) → shared/ — wrap behind a public API (e.g. logError via @shared/lib, main-process logging in @main/shared/logging/).
  • Thin helpers built on the framework that features use everywhere → shared/lib/ (setup stays in app).

Split pattern: A dependency can be both — instance/wiring in app/providers/, usage API in shared/lib/ (e.g. plugin in app, composable in shared).

app

Composition root — app initialization, global providers, and framework wiring under providers/ and main.ts.

  • Contains everything that composes the renderer application; not further abstracted.
  • Examples (project-specific): Vue Router, Pinia instance, UI framework, i18n plugin.
  • Router imports only page components from @pages/*.
  • Router guards may use features/shared for app-wide orchestration (e.g. auth check).
  • Do not register feature or widget UI directly in the router.

pages

Route components — one page per route. The router knows only @pages/*.

  • Pages compose features and optionally widgets; they contain no business logic.
  • Structure per page: ui/, index.ts (public API).
  • Each file in ui/ is a page component: ship <Name>.vue, <Name>.stories.ts, and <Name>.e2e.spec.ts together (see vue-frontend.md).
  • Example: Login.vue binds @features/authentication/signin-user.

widgets

Standalone UI building blocks without feature affiliation (e.g. Navigation). Not a use-case slice.

  • May use features and shared, but should not be imported by pages when mounted globally.
  • Structure: ui/, optional i18n/, index.ts.
  • Each component in ui/ requires colocated <Name>.stories.ts and <Name>.e2e.spec.ts.

features

Business functionality divided into slices (use cases). One slice = one cohesive use case.

Ideal slice structure per use case:

features/<feature>/<slice>/
  ui/          # Vue components for the use case
               # Per component: <Name>.vue + <Name>.stories.ts + <Name>.e2e.spec.ts (all required)
  model/       # composables, state, form logic
  i18n/        # keys.ts, de.ts, en.ts, index.ts (one de/en file each — no language subfolders)
  api/         # use-case API: wraps shared/api for this slice
  service/     # (optional) orchestration within the slice
  index.ts     # public API — export only here

API layering: Generic client access lives in shared/api/ (e.g. Supabase client, generic auth functions). Each slice that needs such access gets its own api/ folder with use-case-specific functions that call shared/api and handle errors/monitoring at slice level.

  • model/ and service/ import only from the slice api/, never directly from @shared/api.

  • Example: register-user/api/sign-up-with-mail-and-password.ts calls signUpByEmailPassword from @shared/api; model/use-register.ts uses the slice function.

  • Cross-slice logic within a feature belongs in features/<feature>/service/ or model/.

  • Slices import shared; they may use other slices of the same feature, but not other features, pages, or widgets.

  • Add new functionality as a new slice; do not mix into existing slices.

shared

Abstraction of swappable dependencies. No feature or page logic. Features/widgets import only the public API — never the SDK directly.

| Segment | Level | Content | | ------------------------------------------------------------ | ---------- | ------------------------------------------------------- | | api/ | Low-level | Client implementations (e.g. Supabase) | | lib/browser/, lib/validation/, lib/pinia/, lib/i18n/ | Low-level | Technical helpers, validation, store utilities | | lib/logging/ | High-level | Error logging via logError (@shared/lib public API) | | ui/ | — | Reusable Vue components without feature affiliation | | model/, store/, types/, errors/ | — | Shared domain types, stores, error classes |

  • Low-level: direct technology abstractions (storage, HTTP client, validators).
  • High-level: domain-near abstractions (logging). Features use @shared/lib logError in api/ layers instead of direct file or IPC access.
  • Vue components without feature affiliation → shared/ui/, not in features.
  • Each component in shared/ui/ requires colocated <Name>.stories.ts and <Name>.e2e.spec.ts.
  • shared imports no features, pages, widgets, or app.

Import aliases

@app/*, @pages/*, @features/*, @widgets/*, @shared/* — always import via aliases, never relative paths across layer boundaries.

Checklist for new files

  1. Correct layer and slice chosen?
  2. app vs. shared: wiring or swappable abstraction?
  3. Import direction respected (downward only)?
  4. Public API exported via index.ts?
  5. i18n keys in slice under i18n/, not in shared?
  6. Reusable UI without feature affiliation → shared/ui/?
  7. API access to shared/api → slice api/ as intermediary, not directly in model/?
  8. New or changed file in ui/ → colocated <Name>.stories.ts and <Name>.e2e.spec.ts added in the same change (not follow-up)?

Related Skills

View on GitHub
GitHub Stars0
CategoryDevelopment
UpdatedNaNy ago
Forks0

Security Score

68/100

Audited on Invalid Date

2 medium1 low