ADR-009: Client Architecture Baseline
Status: Accepted
Date: 2026-03-17
Scope: client-rust/, with emphasis on the maekon-app package (now lives in src-tauri/ — the former crates/maekon-app/ directory was removed by ADR-004 Tauri v2 migration; the package name maekon-app is preserved), maekon-web, integration runtime, and AI provider surfaces
Context
The client architecture has undergone several rounds of structural cleanup across:
- provider surface modeling
- AI runtime wiring
- integration plane runtime design
maekon-appcomposition-root structuremaekon-webdelivery/service layering
Those changes are no longer experimental cleanup. They now represent the intended baseline for future development. Without an explicit ADR, there is a high risk that later work will gradually reintroduce:
- handler fattening
AppStateleakage into delivery code- service-entry drift
- composition-root growth in
setup.rs - non-spec-driven AI provider behavior
- accidental collapse of the external integration plane back into the local control plane
This ADR freezes the current shape as the standard to maintain and improve from.
Decisions
1. Core Layering Remains Stable
The following layer roles are now fixed:
maekon-coreis the domain contract layer.- Adapter crates implement ports from
maekon-core. maekon-appis the composition root and runtime orchestrator.maekon-webis a delivery layer only.- External integration remains distinct from the local desktop control plane.
- AI provider behavior remains surface-driven and contract-driven.
This ADR does not replace ADR-001 or ADR-002. It operationalizes them as the accepted baseline for the current client shape.
2. maekon-web Uses a Fixed Delivery Pattern
maekon-web MUST keep the following structure:
- Handlers stay thin.
- Narrow delivery substates are represented as
WebContextstructs. WebContextdefinitions live inweb_contexts/mod.rs.- Services are the public orchestration entrypoints at the delivery boundary.
- Assemblers and helper modules own DTO shaping and pure transformation logic.
- Handlers and services MUST NOT pull
AppStatedirectly except for explicit cross-cutting exceptions such as middleware.
Required handler flow:
State(WebContext) -> QueryService/CommandService -> Assembler/Helper
Forbidden drift:
- defining new
WebContextstructs inside feature service files - reintroducing
context.queries()andcontext.commands()factory helpers - moving domain invariants into handlers
- letting web-only delivery concerns leak into
maekon-core
Preferred handler boundary:
XxxQueryService::new(context)
XxxCommandService::new(context)
3. maekon-app Keeps Builder/Coordinator Composition
maekon-app MUST preserve the current app-layer composition style.
Required shape:
setup.rsremains close to a pure assembly script.- Runtime bootstrap belongs in app-layer builders and coordinators.
- Long-running orchestration belongs in bundles, runtime coordinators, or launch builders.
This applies to the runtime modules already in use, including:
integration_runtimeagent_runtimeweb_server_runtimebackground_runtimestorage_runtimeupdate_runtime
Forbidden drift:
- growing
setup.rsback into a feature-implementation file - embedding runtime-specific orchestration directly in Tauri setup wiring
- bypassing builders for new runtime slices without a clear architectural reason
4. Integration Plane Remains Separate
The integration architecture is accepted as the correct direction and MUST be preserved.
Required shape:
- Local
/apiremains a first-party control plane. - External integration remains a separate plane with its own auth and runtime model.
- The integration runtime remains outbound and client-initiated.
- Privacy, policy, and audit gates remain mandatory for all external egress.
Required modeling split:
session/authegress/outboxinboxpolicy/audit
These concerns MUST NOT be collapsed into one generic controller.
5. AI Provider Runtime Remains Spec-Driven
The AI/provider architecture is also accepted as baseline.
Required shape:
- Provider behavior is driven from provider surface contracts and catalog specs.
managed_oauth,direct_http,subprocess_cli, and self-hosted surfaces remain modeled as explicit surfaces.- Settings, runtime, and UI consume the same surface contracts.
- New providers should extend the spec-driven path before introducing special-case logic.
Forbidden drift:
- ad hoc vendor branching where a surface contract already exists
- delivery-layer AI behavior that bypasses provider surface resolution
- reintroducing mismatched settings/runtime/provider interpretations
6. Explicit Exceptions Are Allowed but Narrow
The following exceptions are allowed and are not considered violations:
- Middleware may still use
State<AppState>directly for cross-cutting auth and boundary enforcement. - Pure specification helper modules may remain function-oriented when they are not orchestration entrypoints.
- Test modules may construct
AppStatedirectly for fixtures.
These are explicit exceptions and should not be generalized into broader patterns.
Consequences
Positive
- The client now has a clear architectural baseline for future work.
- New work can be judged against stable rules instead of local style preference.
maekon-webis significantly easier to review because handlers, contexts, services, and assemblers have clearer roles.maekon-appis less likely to regress into an oversized composition root.- Integration and AI/provider work can evolve without reopening already-solved structural problems.
Negative
- Some contributors may view the boundary rules as stricter than necessary.
- Small features may require one extra service or helper type compared with a shortcut implementation.
- Middleware and pure helper modules remain explicit exceptions, which requires some judgment in review.
Operational Impact
Future refactors should optimize for real architectural wins, not cosmetic splitting.
This ADR does not mean:
- every helper must become its own type
- every utility must become a service
- refactoring should continue indefinitely
From this point, the default is to extend the product on top of this baseline rather than repeatedly redesign the baseline itself.
Review Checklist
Any substantial change that touches client architecture should still answer:
- Does it preserve DDD and Hexagonal dependency direction?
- Does it keep
maekon-webas a delivery layer? - Does it preserve the
WebContext -> service -> assembler/helperflow? - Does it preserve privacy, policy, and audit gates for integration and automation?
- Does it preserve the spec-driven AI/provider architecture?
- Does it keep runtime wiring in builders/coordinators instead of regrowing
setup.rs?