Agent Skillslatitude-dev/latitude-llm › architecture-boundaries

architecture-boundaries

GitHub

规范应用架构分层与边界,定义客户端、路由及日志配置,明确Web与公共API的职责分离,遵循端口适配器模式与DDD布局。

.agents/skills/architecture-boundaries/SKILL.md latitude-dev/latitude-llm

Trigger Scenarios

设计应用目录结构 划分领域层与接口层 实现多租户或DDD布局

Install

npx skills add latitude-dev/latitude-llm --skill architecture-boundaries -g -y
More Options

Non-standard path

npx skills add https://github.com/latitude-dev/latitude-llm/tree/development/.agents/skills/architecture-boundaries -g -y

Use without installing

npx skills use latitude-dev/latitude-llm@architecture-boundaries

指定 Agent (Claude Code)

npx skills add latitude-dev/latitude-llm --skill architecture-boundaries -a claude-code -g -y

安装 repo 全部 skill

npx skills add latitude-dev/latitude-llm --all -g -y

预览 repo 内 skill

npx skills add latitude-dev/latitude-llm --list

SKILL.md

Frontmatter
{
    "name": "architecture-boundaries",
    "description": "Layering and boundaries, web vs public API, app layout (clients, routes, logging), ports\/adapters, runtime-portable domain\/shared\/utils code, multi-tenancy, DDD layout, or anti-patterns."
}

Architecture and layer boundaries

When to use: Layering and boundaries, web vs public API, app layout (clients, routes, logging), ports/adapters, runtime-portable domain/shared/utils code, multi-tenancy, DDD layout, or anti-patterns.

App boundaries (apps/*)

Apps only handle:

  • Input validation
  • Authentication and authorization
  • Organization access enforcement
  • Routing to domain use-cases

No business logic in handlers, controllers, or jobs.

Application layout (apps/*)

  • Clients: Initialize integrations in apps/*/clients.ts and import from boundaries — avoid scattering raw clients.
  • Routes: Use apps/*/routes/ with a registerRoutes() (or equivalent) pattern so the HTTP surface stays modular.
  • Logging: Use createLogger() from @repo/observability with a stable service name per app.
  • Tracing: Every Effect.runPromise call site must include withTracing from @repo/observability in the pipe chain to connect Effect spans to the OTel pipeline. See effect-and-errors for the full tracing rules.
  • Configuration values: Read env through parseEnv / parseEnvOptional — see env-configuration.

Web vs public API (apps/web, apps/api, @repo/operations)

  • The public API's operation definitions (route config + transport-neutral execute logic) live in packages/operations (@repo/operations) — the boundary-contract layer between apps and domain. One definition fans out to the HTTP route, OpenAPI, MCP tool, SDK methods, CLI command, and in-process agent tools.
  • apps/api is the transport shell: middleware (auth, org context, rate limiting), the MCP HTTP transport, mounting operationModules, and the manifest emit scripts. Treat the operation contracts as externally consumed and evolve them carefully.
  • @repo/operations sits above domain: operations validate input, map to public schemas, and orchestrate @domain/* use-cases — the same boundary responsibilities apps own, factored into a package so non-HTTP consumers (worker-side agents) can run execute in-process.
  • apps/web must not call or proxy through apps/api for internal product features.
  • For web product development, implement backend behavior in apps/web server functions by composing domain use-cases and platform adapters directly.
  • Keep iteration velocity in apps/web by adding web-private server functions/stores while preserving apps/api stability.
  • Shared business rules still belong in domain packages; apps/web and @repo/operations should both orchestrate domain use-cases rather than duplicating policy.
  • Latitude product capabilities should be equally accessible to humans through the web UI and to other LLM agents through MCP/API surfaces.
  • Do not dead-end product behavior into UI-only flows. Preserve the boundary rules above, but design schemas, use-cases, and public capabilities so machine-facing access can exist without redesign.
  • For the concrete recipe — defineOperation, OperationModule manifests, group/sdkMethod/access/rateLimitTier, pnpm openapi:emit / pnpm mcp:emit, schema-description rules that fan out to the TS + Python SDKs, MCP tools, and the latitude CLI, the required declarative access field, and defineToolset (with its access ceiling) for internal agents — see api-endpoints.

Cross-cutting implementation constraints

  • Public request/response schemas should remain boundary-specific; they may reuse shared domain schemas or narrower projections rather than forcing full domain entities onto every surface.
  • When a capability is part of the product contract, preserve a machine-facing MCP/API surface instead of making it web-only.

Domain layer (packages/domain/*)

Business logic lives here. Domain packages expose:

  • Use-cases
  • Canonical entity schemas and inferred entity types
  • Domain types and errors
  • Dependency ports (interfaces/tags)

Domain package layout

Domain entities are Zod-first: entitySchema + z.infer<typeof entitySchema> in src/entities/<entity>.ts. See dev-docs/domain-entities.md and docs/adr/0001-domain-entity-schema-style.md.

  • Treat canonical domain entity schemas as the source of truth. Schemas and types elsewhere in the same domain, plus app/platform boundary schemas, should derive from or reuse the entity shapes whenever practical instead of re-declaring the same fields.
  • When a boundary schema must differ materially from the entity shape, still reuse the relevant domain constants, field schemas, and literal unions rather than hardcoding duplicated lengths or sentinel values again.
  • Canonical entity schemas and their inferred entity types belong in packages/domain/*/src/entities/<entity>.ts.
  • Domain package constants belong in packages/domain/*/src/constants.ts.
  • Domain package errors belong in packages/domain/*/src/errors.ts. A full package-by-package inventory and import rules live in dev-docs/domain-errors.md.
  • For how to structure those errors (tagged classes, HTTP fields, unions per flow, naming), treat packages/domain/issues as the reference: see packages/domain/issues/src/errors.ts and the section Domain errors (@domain/issues reference pattern) in dev-docs/issues.md.
  • Small domain-scoped shared helpers such as predicates or lifecycle helpers belong in packages/domain/*/src/helpers.ts.
  • Types and schemas that exist only as inputs to one domain use-case belong in that use-case file rather than a generic side module, unless several use-cases truly share the exact same contract.
  • App and platform layers should build boundary-specific schemas by reusing or deriving from domain entity/use-case schemas whenever practical rather than redefining the same contract from scratch.

Infrastructure (packages/platform/*)

Infrastructure details live here only. Platform packages implement adapters for domain ports.

Platform adapters: Effect-based clients

Reference implementation: packages/platform/db-weaviate/src/client.tscreateWeaviateClientEffect (and the thin createWeaviateClient wrapper used by scripts).

Use this pattern when a platform package owns an external SDK client so composition roots can stay in Effect and errors stay typed.

  1. Primary constructor is an Effect — Export createXClientEffect(...): Effect.Effect<Client, E, never> (or with requirements R if unavoidable). Scripts and one-off CLIs may export async function createXClient() as Effect.runPromise(createXClientEffect(...)) only at the boundary that needs promises.
  2. Typed errors — Model connection, validation, and bootstrap failures with Data.TaggedError (or shared env errors from @platform/env). Union them into a single CreateXClientError (or similar) exported next to the constructor.
  3. Configuration — Resolve settings with parseEnv / parseEnvOptional from @platform/env inside the Effect pipeline, not ad hoc process.env reads scattered outside the client module.
  4. Interop — Wrap promise-based SDK calls in Effect.tryPromise and map failures to tagged errors. Compose steps with Effect.pipe, Effect.flatMap, and Effect.map.
  5. Bootstrap in the pipeline — If the client must apply schema/migrations/health checks before use, run those as Effects in the same pipeline (see Weaviate: migrateWeaviateCollectionsEffect after connect) so callers get a ready client or a single error channel.
  6. Live layers — Expose a thin XClientLive(client, scope...) layer for the external SDK client and keep repository adapters as Layer.effect or Layer.succeed values that depend on that client service as needed. The composition root acquires the client with createXClientEffect and provides it via a small helper when useful, for example withWeaviate(IssueProjectionRepositoryLive, client, organizationId).

Not every legacy adapter has been migrated; prefer this shape for new work and when touching client construction.

Shared utilities (packages/utils)

General-purpose utility functions that can be shared across any package (domain, platform, or app) live in @repo/utils. This package should contain pure, stateless helper functions with no domain or infrastructure dependencies.

Examples: formatCount, formatPrice, string helpers, number formatters.

When writing a utility function that is not specific to a single domain or package, place it in @repo/utils instead of keeping it local.

Shared domain vs utils

@domain/shared and @repo/utils have different responsibilities and should not be merged.

  • Use @domain/shared for domain-level shared contracts, types, errors, and IDs used across bounded contexts.
  • Use @repo/utils for global pure, stateless helpers that are reusable anywhere.
  • If a helper has domain/business meaning, it belongs in @domain/shared; otherwise, use @repo/utils.

Ports and adapters

  • Domain depends on interfaces/tags only (ports like Repository, CacheStore, Publisher)
  • Platform packages implement adapters
  • Composition roots in apps provide live layers
  • Domain must never import concrete DB/cache/queue/object storage clients
  • Repository method names: Use the standard verbs in dev-docs/repositories.md (findById, findByXxx for unique keys, listByXxx / list for collections, save, delete vs softDelete, etc.).
  • Reliability async contracts should stay project-scoped as well as organization-scoped: include both organizationId and projectId in event/task/workflow payloads by default (except MagicLinkEmailRequested, UserDeletionRequested, domain-events, magic-link-email, and user-deletion payloads).

Web standards first (domain, utils, shared)

In packages/domain/*, packages/utils, @domain/shared, or any code that may run outside Node (browser, edge, isolates), prefer Web Standard APIs over Node-only modules so those layers stay portable.

  • Use crypto.subtle / crypto.getRandomValues instead of node:crypto
  • Use fetch instead of Node-specific HTTP clients
  • Use TextEncoder / TextDecoder instead of Buffer.from(…, 'utf-8')
  • Use Uint8Array for binary data in public interfaces
  • Use ReadableStream instead of node:stream / node:fs streams
  • Use URL, URLSearchParams, Headers, Request, Response from the global scope
  • Use structuredClone instead of JSON round-trips for deep cloning

Node-only APIs are acceptable in build tooling, scripts, CLI utilities, and test infrastructure. If you need Node outside those scopes, add a brief comment explaining why.

Data and infrastructure (overview)

  • Postgres: Control-plane and relational data (users, organizations, memberships, config)
  • ClickHouse: High-volume telemetry storage and analytical reads
  • Weaviate: Vector database for embeddings storage and semantic similarity search
  • Redis: Cache and BullMQ backend
  • Object storage: Durable raw ingest payload buffering

For access patterns, schema, and migrations, see database-postgres and database-clickhouse-weaviate.

Multi-tenancy

  • Every request is organization-scoped
  • A user may belong to many organizations
  • Organization membership checks happen at boundaries before domain execution
  • All telemetry persistence and query paths include organizationId
  • Organization-scoped Redis or cache keys must start with org:${organizationId}:...; keep the org id first in the key

Domain design (DDD)

  • Organize by bounded context (e.g. telemetry, organizations, identity, alerts)
  • Domains should be single-responsibility and focused on policy/rules
  • Use in-memory adapters for fast tests where possible

Anti-patterns to reject

  • Cross-domain logic without clear ownership
  • New provider integrations without a core capability contract
  • Introducing application env vars without the LAT_ prefix (see env-configuration)
  • Using "use client" or "use server" directives — these are Next.js-specific; the web app uses TanStack Start
  • Exporting test utilities from a package's main entry point (see testing)

Version History

  • 2479822 Current 2026-08-20 10:35

Same Skill Collection

.agents/skills/agentation-watch-mode/SKILL.md
.agents/skills/analyze-problem/SKILL.md
.agents/skills/api-endpoints/SKILL.md
.agents/skills/artifact-designer/SKILL.md
.agents/skills/async-jobs-and-events/SKILL.md
.agents/skills/authentication/SKILL.md
.agents/skills/backoffice/SKILL.md
.agents/skills/better-auth-best-practices/SKILL.md
.agents/skills/code-style/SKILL.md
.agents/skills/database-clickhouse/SKILL.md
.agents/skills/database-postgres/SKILL.md
.agents/skills/docs/SKILL.md
.agents/skills/effect-and-errors/SKILL.md
.agents/skills/env-configuration/SKILL.md
.agents/skills/explain-diff-html/SKILL.md
.agents/skills/fix-datadog-issues/SKILL.md
.agents/skills/gh-issue/SKILL.md
.agents/skills/humanizer/SKILL.md
.agents/skills/managing-maintenance-windows/SKILL.md
.agents/skills/mintlify-preview/SKILL.md
.agents/skills/notifications/SKILL.md
.agents/skills/production-release/SKILL.md
.agents/skills/review-pr-comments/SKILL.md
.agents/skills/testing/SKILL.md
.agents/skills/toolchain-commands/SKILL.md
.agents/skills/web-frontend/SKILL.md
.agents/skills/ci-watchdog/SKILL.md
.agents/skills/create-pr/SKILL.md
.agents/skills/temporal-developer/SKILL.md

Metadata

Files
0
Version
2479822
Hash
4eaf906c
Indexed
2026-08-20 10:35

- 위키
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-25 12:15
浙ICP备14020137号-1 $방문자$