Skip to main content

SOLID boundaries eslint spike — API flat services/ fence

Tier: Internal
Status: Frozen (2026-06-07) — RFC-0011 Phase 3 deliverable
Audience: API maintainers, CI
Related: solid-boundaries-eslint-spike.md, pre-flip-application-surface-backbone.md §6.8, LINTING.md


1. Why this spike exists

Trigger: RFC-0011 Wave 3e moved brewery business logic from flat services/api/src/services/ into modules/brewery/services/. B5 fences sibling canonical modules under modules/** but does not stop new brewery files landing back in flat services/ — a regression vector called out in backbone §6.8.2.

Goal: Mechanical lint on flat horizontal services so Wave 3e colocation stays enforced without blocking intentional platform wiring.


2. Out of scope (unchanged)

Documented in solid-boundaries-eslint-spike.md §2:

PatternRationale
services/api/src/services/ai/**modules/*/services/*Horizontal AI advisor reads module services by design
services/api/src/app.tsregister*Module()Boot wiring
modules/**domain/, platform/B5 spike scope

3. Elements

TypePathRole
api-flat-servicesservices/api/src/services/** (excluding ai/** via eslint ignores)Horizontal orchestrators that must stay brewery-free
canonical-module-servicesservices/api/src/modules/*/services/**Module-owned service layer
platform, domain, pluginsSame as B5 spikeAllowed imports from flat services

4. Rule

File glob: services/api/src/services/**/*.{ts,tsx} with ignores: ["services/api/src/services/ai/**"]

Rule: boundaries/element-types at errorapi-flat-services disallows canonical-module-services.

Message: Flat API services must not import canonical module services — colocate in modules/<code>/services/ (RFC-0011 Wave 3e).


5. Allowlist (@arch-boundary)

Legacy orchestrators that intentionally delegate to module services until a follow-on refactor:

FileReason
platformRecipesService.tsPlatform-admin recipes import path; delegates to RecipesService / import parsers

New allowlist entries require @arch-boundary comment + maintainer review — do not expand casually.


6. Test matrix

CaseExpected
platformRecipesService.ts imports modules/brewery/services/*Allow (explicit file override / @arch-boundary)
services/ai/tools/mrp/*.ts imports modules/mrp/services/*Allow (ai/** ignored by fence block)
New services/fooService.ts imports modules/brewery/services/barError
modules/brewery/services/* imports sibling moduleError (existing B5 rule)

7. Verification

./scripts/ci-parity-check.sh --archive run --jobs lint

Configuration: eslint.config.mjs block after SOLID B5.