Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Clarify the Entities vs Features boundary as Read vs Write and add Server‑first guidance (with decision tree, cheat‑sheet, and pitfalls)#863

boaz-hwang started this conversation inIdeas
Discussion options

Summary

This PR proposes a focused clarification to Feature‑Sliced Design (FSD):

  • DefineEntities asread‑only, pure UI/data views (same input → same output).
  • DefineFeatures asall forms of write/interaction (domain writes, URL writes, client‑state writes, and side‑effects).
  • Add a field‑testeddecision tree,rules of thumb,classification tables, andcommon pitfalls to help teams keep boundaries intact.
  • Provide an optional, stack‑agnosticServer‑first appendix (with Next.js/RSC as one concrete mapping).

The proposaldoes not change the layer model, slice/segment rules, or public API rules. It tightens semantics that already exist in FSD and offers pragmatic guardrails that reflect real‑world usage. See FSD layer and slice/segment references for context. ([feature-sliced.design]1)


Motivation

Teams frequently blur boundaries by:

  • AddingonClick or router hooks toEntities.
  • ComputingURLs inside lower layers.
  • Mixing display and interaction, which harms caching, bundle size, and testability.

While FSD defineslayers (app → … → entities → shared) andsegments (ui,api,model, etc.), the docs stop short of a crisp, testableboundary heuristic for Entities vs Features. This PR supplies that heuristic and maps it to modern Server‑first rendering where appropriate. ([feature-sliced.design]1)

Additionally, the current “Routing” issue page (WIP) warns about URL logic leaking into lower layers; this PR contributes concrete guidance and examples to operationalize that page. ([feature-sliced.design]2)


Scope of change (documentation only)

  • New guide: “Read vs Write boundary: Entities (Read) and Features (Write)”

  • New appendix: “Server‑first mapping (Next.js RSC example)”

  • Updates to:

    • Entities reference — clarify purity & allowed links.
    • Features reference — explicitly enumerate “write” categories and ownership.
    • Code smells/Issues → Routing — add prescriptive rules for href ownership and URL builders.
    • Testing guidance (brief) — where snapshot/a11y vs E2E/interaction tests belong.

No normative changes to import rules, slices/segments, or public API. Cross‑imports and@x remain as documented. ([feature-sliced.design]3)


Proposed content (draft)

1) Definitions

Entities (Read)

  • Purpose:Pure, reusable rendering of business concepts.
  • Behavior: Given the same input, returns the same markup (pure view).
  • No local interaction handlers, router mutations, side‑effects, timers, or implicit state.
  • May renderlinks given via props (href computed elsewhere).
  • Typical segments:ui for view,model for view types/schemas,api forread/SELECT helpers if needed. ([feature-sliced.design]1)

Features (Write)

  • Purpose:All user interactions and writes.

  • Categories:

    1. Domain write (server/data): create/update/delete (e.g., “Quiz”, “User”).
    2. State write (client/URL): infinite scroll, filter, sort, search, pagination, selection, clipboard, downloads, prefetch, sockets, etc.
  • Ownshandlers (on*),URL/query changes,side‑effects,optimistic UX,permissions for actions, andtesting of interactions.

  • Typical segments:ui (forms/buttons/observers),model (local state/validators),api (mutations). ([feature-sliced.design]1)

Mapping note (unchanged FSD semantics): Features = “main interactions users care about”, Entities = “business concepts.” This PR sharpens that distinction usingRead vs Write as a practical boundary that teams can test/enforce. ([feature-sliced.design]1)


2) Rules of thumb (operational)

  1. If you see anon* handler, it belongs inFeatures.
  2. If youtouch the router/URL, it’sFeatures (URL is state; changing it is a write).
  3. Links are allowed in Entitiesonly if thehref is provided by upper layers (Pages/Features) and doesnot compute or mutate view conditions.
  4. When unsure, decide by“Who owns the action?” and“Same input → same output?” If it writes (domain/URL/client‑state/side‑effects) →Features.

Routing addendum: avoid hard‑coding URLs in lower layers; pass them down from Pages/Features via props/builders. ([feature-sliced.design]2)


3) Decision tree

  1. Does this component trigger any write?
    (DB, URL, client state, side‑effects like clipboard, prefetch, socket)
    Yes:Features. →No: go to 2.

  2. Is it pure? (Same input ⇒ same markup.)
    Yes:Entities. →No: likelyFeatures decides conditions and injects props to an Entity view.

  3. Is it a link?

    • Resource navigation (no condition write):Entities can render the linkgiven an href prop.
    • View/condition change (filters, panel/modals via query):Features builds and owns the href.

4) Server‑first appendix (optional, stack‑agnostic; Next.js as example)

  • Intent: show one concrete mapping; FSD remains stack‑agnostic.

  • Mapping (Next.js App Router):

    • Entities → Server Components by default (fetch + render on server; cache/stream as appropriate).
    • Features → Client Components (handlers, URL writes) +Server Actions for domain writes.
    • Benefits: smaller bundles, predictable caching, clearer tests. ([Next.js]4)

Minimal folder sketch (fits existing FSD layers/segments):

src/├─ pages|app/ …                          # routing/composition├─ entities/│  └─ <domain>/<read-view>/│     ├─ ui/*.tsx                        # pure views (server-first)│     └─ action/get*.ts                  # SELECT-only helpers (optional)├─ features/│  └─ <feature>/<intent>/│     ├─ ui.tsx                          # client component (handlers)│     ├─ model/state.ts                  # client state (optional)│     └─ action/*.ts                     # server actions (POST/PUT/DELETE)├─ widgets/├─ shared/ (ui|lib|api|config|routes|i18n)

(Still honors layers, segments, public API, and import rules.) ([feature-sliced.design]1)


5) Classification cheat‑sheet (abridged)

  • Entities: resource details/card/table/chartdisplay, static breadcrumbs/nav, static SEO/OG output, audit logview, loading skeletons/empty states (pure), resource links (href passed in).
  • Features: filter/sort/search/pagination/infinite scroll, create/update/delete, optimistic updates, drafts/autosave, file upload/download (generated), selection/bulk actions, tooltips with state, sockets/SSE/presence, prefetch on hover/in‑view, route guards/redirects, A/B switches & analytics, geolocation & permissions, PWA prompts, query‑based modals/panels, payment/queues, toasts & retry, error boundaries with recovery.

6) Testing guidance (brief)

  • Entities → snapshot + a11y; contract tests for props.
  • Features → interaction/E2E (happy path, rollback, routing, side‑effects).

7) Common pitfalls (and fixes)

  • Router/URL in Entities (useSearchParams,router.*) →move to Features, pass props/href down.
  • “Just one onClick” in Entity → becomes a write; move to Feature.
  • Hardcoded URLs in lower layers → push up to Pages/Features; supply href via props/builders. ([feature-sliced.design]2)
  • Wildcard public APIs or cross‑imports without@x → follow Public API rules and@x notation. Consider Steiger for enforcement. ([feature-sliced.design]3)

Backward compatibility

  • No breaking changes.
  • Purely documentary; aligns with existing layers, slices/segments, public API, and routing guidance. ([feature-sliced.design]1)

Alternatives considered

  • Keep Entities/Features definitions as‑is and rely on team conventions.

    • Rejected: leaves teams without a crisp, testable boundary; recurring confusion around links, URL writes, and handlers.

Open questions for maintainers

  1. Where should theURL‑write rule live long‑term — inRouting (Guides/Issues) orEntities/Features references (cross‑linked)? ([feature-sliced.design]2)
  2. Should we include a shortlinters/tooling sidebar (e.g., Steiger rule examples) in Public API/import rules? ([feature-sliced.design]3)
  3. Would you accept aframework‑appendix pattern (Next.js first, then Vue/Nuxt, SvelteKit, Angular) to keep the core spec stack‑agnostic?

Checklist

  • Adds anew guide with decision tree, rules of thumb, and cheat‑sheet.
  • UpdatesEntities/Features references with explicitRead vs Write semantics. ([feature-sliced.design]1)
  • StrengthensRouting guidance with href ownership and examples. ([feature-sliced.design]2)
  • Adds aServer‑first appendix referencing RSC/Next.js docs as one mapping example (optional). ([Next.js]4)
  • No code changes; docs only.

Appendix: References (for reviewers)


If maintainers agree with the direction, I can follow up by splitting this into:

  1. a new guide page (Read vs Write boundary),
  2. a short Entities/Features reference edit, and
  3. a Routing page PR with examples.
You must be logged in to vote

Replies: 1 comment

Comment options

I'm sorry, but this text looks AI-generated to me. If I'm wrong about my assumption, could you please edit the message to explain your thought more briefly?

You must be logged in to vote
0 replies
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
Ideas
Labels
None yet
2 participants
@boaz-hwang@illright

[8]ページ先頭

©2009-2025 Movatter.jp