385 lines
8.8 KiB
Markdown
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
|