Skip to content

app-logic: split dto.rs into dto/ modules by domain, without a dependency on state #868

Open

Description

@smart-ssh-planner

Goal

The data transfer types between backend and frontend are grouped by domain in small files, and dto depends only on core types, not on application state. This removes a set of module cycles inside app-logic.

Context

crates/app-logic/src/dto.rs has 2,762 lines (production ~1,810 before the tests at :1812) with 66 types from every domain plus logic:

  • servers and auth: LOCAL_SERVER_ID/is_local :39–56, ServerDto :57, StoredHostKeyDto, KeyFileFactsDto, KeychainStatusDto, AuthMethodKind :167–302, ServerInput :696, normalize_sftp_server_path :752, normalize_start_directory_input :773, AuthMethodInput :795, TestConnectionResult/TestConnectionReport :894–949, UnusableServerDto etc. :629–695, DeleteServerResult :603;
  • AI providers: AiProviderConfigDto :303, AiProviderConfigInput :346 (with validation, MAX_TOKENS_OVERRIDE_UPPER_BOUND :379), credential_ref_for :480;
  • rules and explanations: PatternType :950, pattern_from_parts :969, RuleInput :990, RuleDto :1023, PatternDto, EvalContextInput, EvaluationTraceDto, suggestion and coverage DTOs :1144–1273;
  • chat: HostKeyUserDecision, ActionUserDecision, ActionOrigin :497–549, DocumentFormat :1274, session summaries :1289–1354, ChatHistoryEntryDto and related :1355–1586;
  • groups: GroupDto, KeepGroupResult, DeleteGroupResult :550–602;
  • notes: NoteEditorDto, NoteRevisionDto :838–893;
  • SFTP and elevation: DeletePreviewDto :1587, elevation DTOs :1597–1686, DownloadResultDto, LocalFilePreviewDto, EditSessionDto, RemoteEntryDto :1687–1761, format_unix_permissions :1762, sort_remote_entries :1804;
  • meta: AppInfoDto :1781.

Its only dependencies on other app-logic modules are crate::state::SessionId (:27), crate::events::ConnectionStatus (:25) and crate::server_credentials::sudo_password_credential_ref (:26). Direct two-module cycles in app-logic (counted over crate:: paths, comments excluded) are 15 today, among them dto↔state and dto↔events. External crates use app_logic::dto::is_local and app_logic::dto::DocumentFormat.

Requirements

  • dto.rs becomes dto/mod.rs with pub use re-exports of every public item, plus dto/{server.rs, ai_provider.rs, rules.rs, chat.rs, groups.rs, notes.rs, sftp.rs, elevation.rs, meta.rs} (adjust the grouping where a type clearly belongs elsewhere). Every existing path app_logic::dto::X keeps working.
  • Logic that is not data mapping moves to the domain module that uses it: normalize_sftp_server_path/normalize_start_directory_input (to servers.rs or start_directory.rs), pattern_from_parts (to filter_rules.rs), sort_remote_entries and format_unix_permissions (to a file-browser module); keep a re-export in dto if anything outside the crate uses them.
  • Remove the dependencies dto → state, dto → events and dto → server_credentials: move SessionId and ConnectionStatus into a small leaf module (e.g. crate::ids / crate::status) that state/events re-export, and pass the credential ref in from the caller or move that conversion out of dto.
  • Add a test (in crates/app-logic/tests/ or a #[test] in the crate) that scans crates/app-logic/src for direct two-module cycles over crate:: paths (comments excluded) and fails when the count exceeds a constant ceiling; set the ceiling to the count after this change. Document the counting rule in the test.
  • Tests in dto.rs move with the code they test.

Acceptance Criteria

  • grep -rn "crate::state\|crate::events\|crate::server_credentials" crates/app-logic/src/dto returns nothing.
  • Every file under crates/app-logic/src/dto/ is below 800 lines (production).
  • The cycle test exists, passes, and its ceiling is below 15.
  • cargo build --workspace passes with no change outside app-logic other than import paths, and every previous app_logic::dto::* public path still resolves.
  • Test count per binary unchanged; all pass.
  • Gate green.

Technical Notes

  • The DTOs are the IPC contract with the frontend (src/types.ts); serde attributes (rename_all, tags, defaults) must not change. Add a serialisation snapshot test for a few representative DTOs if none exists, before moving them.
  • ActionUserDecision/HostKeyUserDecision are part of the confirmation path; moving them must not change their serde form.
  • Restructuring only: behaviour, error codes, emitted events, log fields and messages stay identical. Fix nothing on the way; a bug you find goes into its own issue (mention it in the PR).
  • Tests: cargo test --workspace -- --list before and after; the number of tests per test binary does not decrease, and the same tests pass. Tests may move with the code they test (<module>/tests.rs or <module>/tests_<topic>.rs; no new *_tests.rs names).
  • Public items used from outside the crate keep their paths (re-export if an item moves); check with grep -rn over the workspace.
  • New code comments in English; existing German comments may move unchanged.
  • Update docs/architecture.md (module table "Ausnahmen von der Regel (A4)" and the list of app-shell modules) when a module moves between app-shell and app-logic.
  • Gate: cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace, cargo build --workspace (the last one catches production calls into test-support-only items).

Spec impact: none

Related Work

Activity

  1. added this to the 0.8.0 milestone on Oct 26, 2026
  2. 21 remaining items

  3. marked this as blocking 15 issues on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    Priority

    P2 - Normal

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions