docs(architecture): record the main↔renderer import boundary and bucket-root anti-pattern

main-process-architecture.md §3: state the no-renderer-imports rule (src/main + src/preload must not import renderer code; cross-process types → @shared, main-only → src/main), now enforced by the ESLint no-restricted-imports rule; clarify that the existing 'no automated enforcement yet' note refers to the internal direction edges. §7: track the lone remaining exception — main/utils/language.ts's relative renderer i18n import, deferred to the i18n migration PR.

renderer-architecture.md §7: add the anti-pattern of importing a shared bucket root (@renderer/utils, @renderer/types) instead of the specific file/topic, or giving types/ or utils/ a re-export root index.ts.
This commit is contained in:
fullex
2026-06-20 01:10:20 -07:00
parent dc5ba8539b
commit 1a8d57d432
2 changed files with 4 additions and 1 deletions

View File

@@ -64,10 +64,11 @@ The charters imply a direction; dependencies flow toward the business-agnostic f
- **`ai/` is foundational within the business tier**: `features/` and `services/` may depend on it; it must not import a feature.
- **Feature domains are mutually isolated**: a `features/<domain>/` must not import a **sibling** feature — share through `services/`, `ai/`, `data/`, or `@shared`.
- **`ipc/` is the boundary adapter**: it resolves services through the DI container (`application.get`) rather than importing domain modules directly.
- **No renderer imports**: `src/main` and `src/preload` must not import renderer code. Cross-process types live in `@shared`, main-only types stay in `src/main` — see [Shared Layer Architecture](./shared-layer-architecture.md) for placement. Enforced by an ESLint `no-restricted-imports` rule banning `@types` / `@renderer` in `src/main` + `src/preload`; §7 tracks the one remaining exception.
Two dependencies cut across **every** directory and are **not** layering edges — they are ambient infrastructure access: `@logger` (logging) and `@application` (the DI container / service locator). A raw import scan shows almost everything "depending on `core`" only because of these two; the rules above concern direct module imports between domains.
There is no automated boundary enforcement yet (unlike the `import/no-restricted-paths` zones proposed for the renderer in [Renderer Architecture §5](./renderer-architecture.md)); direction is held by convention and review.
There is no automated enforcement of the **internal** direction edges above yet (unlike the `import/no-restricted-paths` zones proposed for the renderer in [Renderer Architecture §5](./renderer-architecture.md)); direction is held by convention and review. The **external** main↔renderer boundary (no renderer imports) *is* enforced — see the rule above.
## 4. Closed Top-Level Governance
@@ -115,6 +116,7 @@ This page describes the **target**. Where current code does not yet match it, th
|---|---|---|
| `utils/index.ts` | a bucket-root `index.ts` holds loose helpers (`debounce`, `makeSureDirExists`, `toAsarUnpackedPath`, …) — the junk-drawer root barrel §2.1 forbids | split into named topic files (`utils/<topic>.ts`); `@shared` has already done exactly this — see [Shared Layer Architecture §6](./shared-layer-architecture.md) |
| legacy `ipc.ts` | v1 IPC registration at the process root, coexisting with IpcApi | domains migrate into `ipc/` (IpcApi) incrementally until `ipc.ts` is retired (§1) |
| `utils/language.ts` i18n | imports renderer i18n JSON via relative path (`../../renderer/i18n/locales/*`, `translate/*`) to build the main-process `t()` table — a main→renderer edge (§3 No-renderer-imports) the ESLint rule does not yet catch (relative `**/renderer/**` is intentionally left unbanned so this still compiles) | relocate the locale JSON to a process-neutral on-disk resource loaded per-process via `i18next-fs-backend`, so main reads its own slice. Marked `TODO(i18n-migration)` in the file; once done, tighten the ESLint group to also ban relative `**/renderer/**` |
By-design edges are **not** deviations and are deliberately omitted: the `data/migration/v2/` migrators reading domain data (§1) and `@logger` / `@application` ambient access from any tier (§3).

View File

@@ -138,6 +138,7 @@ After decomposition every edge is downward (`component → component`/`hook`, `h
- Treating a cross-cutting capability as a peer feature.
- Opening a new top-level directory for a single capability.
- A feature using `export *`, or an external consumer deep-importing a feature's internals.
- Importing a shared bucket root (`@renderer/utils`, `@renderer/types`) instead of the specific file/topic, or giving `types/`/`utils/` a re-export root `index.ts` (§5).
- A hand-rolled `components/layout/` bucket — "layout" is not a layer here: route layouts live in `routes/` (TanStack layout routes), layout primitives (`Box`/`Stack`/`Grid`) in `packages/ui`, app shell in `windows/`.
## 8. Target vs Current State