Skip to content

ADR 0001: Layering, plugin boundaries, and monolith-first composition

ADR 0001: Layering, plugin boundaries, and monolith-first composition

Section titled “ADR 0001: Layering, plugin boundaries, and monolith-first composition”

Accepted

Agent Detective is a self-hosted integration hub: webhooks and plugins normalize work into TaskEvents, use local repository context, and optionally drive PR workflows. The codebase is a pnpm monorepo with a root Express app and workspace packages (jira-adapter, local-repos-plugin, pr-pipeline, etc.).

We want:

  • Clear ingress vs workflow vs outbound integrations over time (multiple issue trackers, same PR engine).
  • Familiar structure for contributors used to hexagonal, onion, or layered architectures in private repos—without adopting heavy DDD ceremony.
  1. Monolith-first — One process, explicit plugin loading (createPluginSystem). No microservices unless multi-tenant hosting demands it later.

  2. Ports in @agent-detective/types — Shared contracts stay in the types package; plugins implement or consume them via the service registry. Avoid compile-time imports from another plugin package for interfaces (prefer types + getService).

  3. Per-package layering — Inside each plugin package we use four logical layers where it helps:

    • presentation — HTTP / Express controllers
    • application — use cases, handler orchestration, plugin Zod options
    • domain — pure transforms and plugin-local domain types (no I/O)
    • infrastructure — external systems (Jira REST, Git host APIs, mocks)

    Concrete folder layout is documented in architecture-layering.md.

  4. Hexagonal mental model — Driving adapters (webhooks) call application code; application code depends on ports (types + AgentRunner + registered services); driven adapters (Jira client, GitHub PR) live in infrastructure.

  • Positive: Easier navigation, clearer place for new adapters, aligns with future “second ingress” work (Linear, GitHub Issues) without a single flat src/ dump.
  • Negative: Deeper paths and import churn when refactoring; mitigated by doing one package at a time and keeping entry index.ts thin.