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

8.8 KiB

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

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:

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

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