agent-ecosystem/docs/FEATURE_ARCHITECTURE_STANDARD.md
2026-05-11 11:09:17 +03:00

385 lines
8.8 KiB
Markdown

# Feature Architecture Standard
**Status**: team standard
**Reference implementation**: `src/features/recent-projects`
This document defines the default architecture for medium and large features in this repository.
## Goals
- keep business rules isolated from Electron-specific runtime details
- make features easier to scale, test, and review
- keep renderer code closer to browser and Tauri portability
- enforce architecture with tooling, not only with code review comments
## Canonical Template
```text
src/features/<feature-name>/
index.ts
contracts/
index.ts
core/
domain/
application/
main/
index.ts
composition/
application/
adapters/
input/
output/
infrastructure/
preload/
index.ts
renderer/
index.ts
```
Use this template by default when a feature:
- spans more than one process boundary
- introduces its own use case or business policy
- needs its own transport bridge or integration surface
- is expected to grow with new providers, sources, or presentation flows
`index.ts` and `main/application/` are optional. Add them only when they have a
clear public or runtime-orchestration role.
## Layer Responsibilities
### `index.ts`
Optional root public barrel for the feature.
Use it for:
- stable type re-exports from `contracts/`
- small pure facades that are intentionally shared across layers
- feature factories when the root barrel is intentionally main-owned and
imported only from main-process code
Not allowed:
- accidental wildcard exports from implementation folders
- mixing browser-safe exports with main-only exports without making process
ownership obvious
- replacing the layer entrypoints when callers need a process-specific surface
### `contracts/`
Cross-process public API for the feature.
Allowed content:
- DTOs
- API fragment types
- IPC or route constants
Not allowed:
- store access
- Electron APIs
- business orchestration
### `core/domain/`
Pure business rules and invariants.
Examples:
- merge policies
- provider-agnostic models
- selection rules
- dedupe logic
Not allowed:
- infrastructure access
- framework access
- side effects
### `core/application/`
Use cases and ports.
Examples:
- orchestration flow
- output ports
- cache ports
- source ports
- response models
Not allowed:
- Electron, Fastify, React, Zustand, child processes
### `main/composition/`
Feature composition root in the main process.
Responsibilities:
- instantiate infrastructure
- wire adapters
- wire use cases
- expose a small facade to app shell entrypoints
### `main/adapters/input/`
Driving adapters for the main process.
Examples:
- IPC handlers
- HTTP route registration
Responsibilities:
- translate transport input into use case calls
- keep transport concerns out of use cases
### `main/application/`
Optional main-process application services.
Use this only when code is too runtime-aware for `core/application`, but is not a
transport adapter or low-level infrastructure helper.
Examples:
- main-only readers that orchestrate runtime services
- process-aware tracking or coordination helpers
Not allowed:
- IPC or HTTP handler registration
- renderer or preload dependencies
- pure domain policy that belongs in `core/domain`
### `main/adapters/output/`
Driven adapters that implement application ports.
Examples:
- presenters
- source adapters
Responsibilities:
- translate between external data and core models
- stay thin around infrastructure helpers
### `main/infrastructure/`
Concrete technical implementation details.
Examples:
- file system adapters
- JSON-RPC transport clients
- binary discovery
- cache implementation
- git identity helpers
Responsibilities:
- know about runtime, process, OS, or protocol details
### `preload/`
Thin transport bridge between renderer and main.
Responsibilities:
- expose a feature API fragment
- depend on `contracts/`
Not allowed:
- main composition code
- renderer logic
### `renderer/`
Feature presentation and interaction.
Recommended structure:
```text
renderer/
index.ts
adapters/
hooks/
ui/
utils/
```
Responsibilities:
- `ui/` renders
- `hooks/` orchestrate interaction and transport usage
- `adapters/` transform DTOs into view models
- `utils/` contain small pure renderer helpers
## Import Rules
### Public entrypoints only
Outside the feature, import only:
- `@features/<feature>` when the feature owns a deliberate root public barrel
- `@features/<feature>/contracts`
- `@features/<feature>/main`
- `@features/<feature>/preload`
- `@features/<feature>/renderer`
Do not deep-import feature internals from app shell or from other features.
Layer entrypoints should be explicit `index.ts` files that export only supported
surface area. Focused tests may import internals when they are testing that unit
directly, but production integration code should not.
### Core isolation
`core/domain` must not import:
- `@main/*`
- `@renderer/*`
- `@preload/*`
- adapters
- infrastructure
- Electron APIs
- Fastify
- child process modules
`core/application` must not import:
- `@features/<feature>/main/**`
- `@features/<feature>/renderer/**`
- `@main/*`
- `@renderer/*`
- `@preload/*`
- Electron APIs
- Fastify
- child process modules
### UI isolation
`renderer/ui` must not import:
- `@renderer/api`
- `@renderer/store`
- `@main/*`
- Electron APIs
Push transport and store access into feature hooks or adapters.
## Browser and Tauri Friendly Guidance
The default transport direction should be:
`renderer -> feature contracts -> app api abstraction -> preload/http adapter`
This keeps renderer code closer to:
- browser mode through HTTP adapters
- a future Tauri bridge
- alternative shells with minimal feature rewrites
To keep that path clean:
- never call `window.electronAPI` directly inside feature UI or hooks
- go through shared renderer API adapters
- keep Electron-specific concerns in `main/` and `preload/`
- keep business rules in `core/`
## When To Use The Full Slice
Use the full template when a feature has:
- its own business rules
- its own merge or filtering policy
- transport wiring
- more than one adapter
- a roadmap beyond a one-off screen tweak
## When A Thin Slice Is Enough
A smaller feature may skip `core/` and `preload/` when it is:
- purely presentational
- only reshaping already-owned data
- not adding a new use case
- not adding a new transport boundary
If the feature still owns meaningful pure semantics or projection rules, keep
`core/` and skip only the process layers you do not need.
Example:
- `src/features/agent-graph` keeps `core/domain` and `renderer`, but does not add fake `main/` or `preload/` folders because the transport boundary lives elsewhere.
## Current Feature Shape Examples
Use these local examples before inventing a new variant:
- `src/features/recent-projects` - full cross-process reference with
`contracts`, `core`, `main`, `preload`, and `renderer`.
- `src/features/member-work-sync` - full cross-process feature with a root
public barrel and broader main-process infrastructure.
- `src/features/member-log-stream` - full cross-process feature that uses
`main/application/` for main-only runtime orchestration.
- `src/features/agent-graph` - thin renderer integration with `core/domain` and
`renderer`, no fake process layers.
- `src/features/codex-model-catalog` and `src/features/team-runtime-lanes` -
process-limited features that omit renderer or preload layers when they do not
own that boundary.
## Definition Of Done For A Reference Feature
A feature is reference-quality when:
- structure matches the full or thin template chosen for the feature
- core is side-effect free
- app shell imports only public entrypoints
- renderer UI is dumb and presentational
- at least the main domain and application rules are tested when those layers
exist
- architecture is enforced by lint rules
- feature has a concise standard or plan doc if it introduces a new pattern
## Recommended Test Coverage
For medium and large features, cover at least:
- domain policy tests
- application use case tests
- critical renderer interaction utilities
- one adapter-level mapping test
## Recent Projects As The Reference
`src/features/recent-projects` is the first slice that follows this standard end-to-end.
Use it as the example for:
- contracts ownership
- core/application separation
- composition-root wiring
- renderer dumb UI + hook orchestration
- browser-friendly transport direction
- feature-level lint guard rails
## Agent Graph As The Thin-Slice Reference
`src/features/agent-graph` is the thin-slice example for a renderer integration
feature built on top of a reusable package.
Use it as the example for:
- keeping pure graph semantics in `core/domain`
- exposing a renderer-only public entrypoint
- integrating `packages/agent-graph` without inventing fake process layers
- migrating legacy `src/renderer/features/*` code into the canonical feature root