feat(nx-plugin): enforce module boundaries on new arch#16803
Merged
feat(nx-plugin): enforce module boundaries on new arch#16803
Conversation
Contributor
❌ Action Required: Monitored Files ChangedThe following files in monitored folders have been modified:
Action Required: Please rebase your branch against git rebase origin/develop |
Contributor
There was a problem hiding this comment.
Pull request overview
Enforces module-boundary rules for the “new architecture” packages (domain/, shared/, features/) by inferring Nx project tags from folder layout and validating workspace-to-workspace dependencies in CI.
Changes:
- Extend the existing Nx project-tags plugin to infer
scope:domain,scope:shared,type:domain-entity,type:domain-apifrom folder layout (with node:test coverage). - Add a Node-based Nx project-graph validator (
tools/nx-plugins/enforce-boundaries) and wire it as a cacheable root Nx targetlint:boundaries. - Add a CI step to run the new boundaries target, plus a local
pnpm lint:boundariesscript and an explanatory note inAGENTS.md.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/nx-plugins/project-tags/plugin.js | Adds tag inference for domain/ + shared/ packages. |
| tools/nx-plugins/project-tags/plugin.test.js | Node unit tests covering new tag inference + regressions. |
| tools/nx-plugins/enforce-boundaries/constraints.js | Defines the dependency-constraint ruleset (Nx-like depConstraints shape). |
| tools/nx-plugins/enforce-boundaries/validate.js | Walks Nx project graph and reports boundary violations (CLI + exported findViolations). |
| tools/nx-plugins/enforce-boundaries/validate.test.js | Node unit tests covering allowed/forbidden dependency edges. |
| project.json | Adds root Nx target lint:boundaries to run the validator. |
| package.json | Adds lint:boundaries convenience script. |
| .github/workflows/test-libs-reusable.yml | Adds CI step to enforce module boundaries (blocking). |
| AGENTS.md | Documents the new boundaries rule and where tags/constraints live. |
| .changeset/nx-enforce-module-boundaries-new-arch.md | Changeset describing the new enforcement behavior. |
gre-ledger
reviewed
Apr 24, 2026
Extends the project-tags plugin so every package under domain/, shared/, and features/ carries the tags that LIVE-29780's upcoming module-boundary validator will rely on: - domain/entity/* -> scope:domain + type:domain-entity - domain/api/* -> scope:domain + type:domain-api - shared/* -> scope:shared - features/* -> scope:features (unchanged) Legacy libs/, apps/, e2e/, and tools/ inference is untouched. 8 node:test assertions lock both the new branches and the legacy regression paths. Refs: LIVE-29780
db34aa9 to
059afc9
Compare
Contributor
|
Contributor
Web Tools Build Status
|
ysitbon
added a commit
that referenced
this pull request
Apr 29, 2026
Per @gre-ledger feedback on PR #16803: the section is too verbose for AGENTS.md and the "Legacy libs/apps/..." line risks confusing agents working in those folders during the migration. Boundary rules already live in the LIVE-29780 ticket description and in tools/nx-plugins/enforce-boundaries/constraints.js. Refs: LIVE-29780
Adds a linter-agnostic validator that walks the Nx project graph and verifies every workspace -> workspace edge against the new-arch layering rules. Runs via @nx/devkit's createProjectGraphAsync, no ESLint involved (the repo is migrating to oxlint; 143/149 packages already use it). Rules (DEP_CONSTRAINTS, defined in tools/nx-plugins/enforce-boundaries/ constraints.js): - scope:shared can depend on scope:shared only (leaf layer) - scope:domain can depend on scope:domain + scope:shared - scope:features can depend on scope:features + scope:domain + scope:shared - type:domain-entity can depend on type:domain-entity + scope:shared - type:domain-api can depend on type:domain-entity + type:domain-api + scope:shared
94951ec to
cff540c
Compare
…heck workflow inferTags: drop the duplicate domain/ and shared/ blocks. The type-tag-aware blocks higher up already emit scope:domain / scope:shared (alongside type:domain-entity / type:domain-api), so behavior is preserved — just clearer to read. CI: extract the boundary check into its own reusable workflow at .github/workflows/test-boundaries-reusable.yml and call it from build-and-test-pr.yml as a dedicated enforce-boundaries job, gated on PRs touching domain/, shared/, or features/. Removes the now- redundant step from test-libs-reusable.yml. The new workflow is temporarily called via @ci/nx-enforce-module-boundaries-new-arch so this PR can prove it runs end-to-end. MUST be flipped back to @develop before merge. Refs: LIVE-29780
A source project can match multiple depConstraints (e.g. a type:domain-api project also has scope:domain). Before this fix findViolations emitted one violation per matching rule, so the same forbidden edge could surface twice in CI output and inflate the violation count. Now keyed by (sourceName, target): one entry per offending edge with the failing source tags accumulated. Output formatting joins the source tags with a comma. Adds a node:test case covering the multi- tag dedup; existing single-tag tests adjusted from sourceTag (string) to sourceTags (array). Refs: LIVE-29780
|
lewisd5
approved these changes
Apr 30, 2026
LL782
reviewed
Apr 30, 2026
LL782
reviewed
Apr 30, 2026
LL782
approved these changes
Apr 30, 2026
LL782
reviewed
Apr 30, 2026
3 tasks
gre-ledger
approved these changes
Apr 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



✅ Checklist
npx changesetwas attached.node --testassertions (8 project-tags + 15 boundary validator). End-to-end run on current tree is green with no pre-existing violations, so CI can land as blocking from day one.enforce-boundariesjob in.github/workflows/build-and-test-pr.ymlcalling the dedicated.github/workflows/test-boundaries-reusable.ymlreusable workflow, gated on PRs touchingdomain/,shared/, orfeatures/— fails the PR on any boundary violation.pnpm lint:boundariesscript for local verification.scope:domain,scope:shared,type:domain-entity,type:domain-apitags to new-arch packages via the existingtools/nx-plugins/project-tags/plugin.js.📝 Description
Locks the dependency graph of the new architecture (
domain/,shared/,features/) before it drifts. Implements LIVE-29780 — the Jira description carries the full plan and the first comment on the ticket carries the research notes.Tag taxonomy — inferred dynamically from the folder layout. No manual
project.jsontags.domain/entity/*scope:domain,type:domain-entitydomain/api/*scope:domain,type:domain-apishared/*scope:sharedfeatures/*scope:features(unchanged)Dependency constraints:
```
scope:shared → [scope:shared] (leaf)
scope:domain → [scope:domain, scope:shared]
scope:features → [scope:features, scope:domain, scope:shared]
type:domain-entity → [type:domain-entity, scope:shared] (entities can't import APIs)
type:domain-api → [type:domain-entity, type:domain-api, scope:shared]
```
Legacy
libs/,apps/,e2e/,tools/stay unconstrained during migration — rules only fire when a project carries a matching source tag.Enforcement — a Node validator at
tools/nx-plugins/enforce-boundaries/validate.jswalks the Nx project graph via@nx/devkit'screateProjectGraphAsyncand checks every workspace→workspace edge againstDEP_CONSTRAINTS. Wired as a cacheable Nx target on the dedicatedenforce-boundariesproject attools/nx-plugins/enforce-boundaries/. CI callspnpm exec nx run enforce-boundaries:lint:boundariesfrom the dedicatedenforce-boundariesjob inbuild-and-test-pr.yml(which usestest-boundaries-reusable.yml). No ESLint involvement — stays aligned with the oxlint migration (143 of 149 packages with alintscript are already on oxlint).Forward compatibility —
DEP_CONSTRAINTSshape (sourceTag/onlyDependOnLibsWithTags) mirrors@nx/enforce-module-boundariesso the array ports verbatim to.oxlintrc.jsonwhen nrwl/nx-labs#441 publishes and oxlint's user-plugin loader stabilizes. Migration at that point: deletetools/nx-plugins/enforce-boundaries/and.github/workflows/test-boundaries-reusable.yml, add.oxlintrc.json, swap theenforce-boundariesjob to@nx/oxlint:lint. One commit.Why not ESLint / oxlint-labs today — summarized above; full research (four alternatives, with rejection rationale and verification steps) is in the Jira comment on LIVE-29780.
❓ Context