Skip to content

feat(frontend): a domain may not reach into another domain's innards either - #1317

Merged
ExtraToast merged 1 commit into
mainfrom
refactor/the-rule-reaches-the-domains
Sep 18, 2026
Merged

ExtraToast merged 1 commit into
mainfrom
refactor/the-rule-reaches-the-domains

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Closes #1263.

Why

ADR-001 states two rules, and the second one — a domain is entered through its index.ts — was enforced everywhere except inside the domains themselves. #1206 landed it scoped to src/pages/** and src/components/**, so a page reaching past a door was a red build while a domain doing the same thing was nobody's error. The one place the rule exists to protect was the one place nothing watched.

That gap is also what is left of #1264, which was closed as a miscount: its "20 cross-domain imports" turned out to be domains reaching into their own innards, which the rule permits.

What this achieves

A domain's internals are free to move again. Until now that was only true against pages and components; a sibling domain could reach past the door and pin the layout behind it, and nothing would say so.

The allowlist does not grow. yarn lint --max-warnings=0 exits 0 on an unchanged tree, so this is a rule landing with no debt attached — measured on ec0e11212, the 20 deep imports under src/domains are every one of them a domain reading its own files, and the only two genuine cross-domain imports, domains/boards/island/BoardMemberDialog.vue and domains/esports/island/LineupEditor.vue, already go through @/domains/user.

How

It cannot be one pattern. "Another domain" is relative to the file doing the importing, and eslint's config has no way to say "any domain except the one you are in" in a single regex. So there is one block per domain: files: ['src/domains/<d>/**'] with a regex that excuses <d> by name and refuses the rest.

The domain list is read off the directory with readdirSync rather than written out here, so a new domain is covered the day it exists rather than the day somebody remembers this file.

The blocks do not clobber the rule above them, or each other. A config block naming a rule replaces an earlier block's options only for files that match both, which is the trap that let a client import through the first draft of #1183 — every files here is disjoint from src/pages/**, from src/components/** and from every other domain.

Not in scope

The .vue exemption stays. A component is imported where it is drawn, and routing components through a barrel loads a domain's whole surface into anything that renders one — which broke two unit suites and, measured, saved nothing in the bundle. A cross-domain .vue import is still legal, and the new block says so in a comment rather than leaving the negative lookahead to read as an oversight.

Nothing is migrated here. The seven pages and thirty-five components still on CROSSES_THE_BOUNDARY are #1257 through #1262 and #1274 through #1276.

Worth a reviewer's attention

The eleven blocks are generated, so the thing to review is the regex rather than the repetition: ^@/domains/(?!<d>/)[a-z]+/(?!.*\.vue$).+. The first lookahead is what makes a domain's own innards legal; the second is the .vue exemption.

One knock-on: #1266 says it deletes "whatever exception #1263 introduces". This introduces none, so that half of #1266 is already satisfied and only the shared allowlist is left for it.

Verification

yarn lint --max-warnings=0 exits 0 on the branch with no allowlist change.

The rule was probed all four ways round rather than assumed, each with a scratch file that was deleted afterwards:

  • src/domains/boards importing @/domains/committees/adapters/committees → error, with the message naming the door
  • src/domains/boards importing @/domains/boards/adapters/boards → allowed
  • src/pages importing @/services/api → still error
  • src/pages importing @/domains/user/adapters/user → still error

The last two are what confirm the new blocks did not replace the existing entry's options.


Diff breakdown added removed, scaled to the largest row.

frontend                                           +35     -0    1
  build & config     ██████████████████████████    +35     -0    1

──────────────────────────────────────────────────────────────────
total (hand-written)                               +35     -0  1 file

…either

The deep-import rule was scoped to pages and components, so the half of
ADR-001 it enforces held for everything except the domains themselves. A domain
reaching past another domain's index.ts was unchecked.

It cannot be one pattern, because "another domain" is relative to the file doing
the importing: there is one block per domain, each naming only its own files and
excusing only its own name. The list is read off the directory rather than
written here, so a new domain is covered the day it exists rather than the day
somebody remembers this file.

The blocks do not clobber the rule above them or each other — a config block
naming a rule replaces an earlier one only for files that match both, and every
`files` here is disjoint from pages, from components and from every other
domain. Probed all four ways round: a cross-domain deep import is refused, a
domain's own innards are not, and both halves of the existing rule still fire.

No allowlist entry was needed. The only two cross-domain imports in the tree,
BoardMemberDialog and LineupEditor, already go through `@/domains/user`.
@ExtraToast ExtraToast added the enhancement New feature or request label Sep 18, 2026
@ExtraToast
ExtraToast merged commit 1aa16c4 into main Sep 18, 2026
28 checks passed
@ExtraToast
ExtraToast deleted the refactor/the-rule-reaches-the-domains branch September 18, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The cross-domain rule lands with its own allowlist

1 participant