Define Architecture
Define durable, easy-to-change architecture defaults for TypeScript apps.
How to use this skill
- Determine context:
- New codebase: follow
Architecture setup workflow. - Existing codebase: follow
Adoption workflow.
- New codebase: follow
- Produce an architecture brief using
Output template. - Run
Validation loopbefore finalizing.
Load references only when needed:
- Stack defaults: references/stack-defaults.md
- Shipping and rollout: references/shipping-practices.md
- Engineering quality checklists: references/craftsmanship.md
Architecture setup workflow
- Define constraints first:
- Product scope, team size, compliance/security needs, expected scale.
- Deployment targets and required integrations.
- Choose repo shape:
- Use
apps/for deployable surfaces (api,web,admin). - Use
packages/for shared libraries (shared,ui,icons,auth,proto).
- Use
- Define backend module contracts:
handler: transport only.service: business orchestration.dao: database access only.mapper: DB/proto/domain transformations.constantsandtypes: module-local contracts.
- Define request context and middleware:
- Use AsyncLocalStorage-backed
RequestContext:import { AsyncLocalStorage } from "node:async_hooks"; type RequestContext = { tenantId: string; userId: string; traceId: string }; const store = new AsyncLocalStorage<RequestContext>(); export const getContext = () => store.getStore()!; export const runWithContext = (ctx: RequestContext, fn: () => void) => store.run(ctx, fn); - Initialize context in every entrypoint (RPC, HTTP, jobs, CLI).
- Read context via
getContext(); do not thread context params through business functions. - Require route policy per RPC method and register services through
registerServiceWithPolicies. - Keep auth, logging, errors, and context in shared middleware.
- Use AsyncLocalStorage-backed
- Define frontend boundaries:
- Default to Server Components; add
"use client"only for client-only behavior. - Use TanStack/Connect Query for server state.
- Use MobX only for cross-cutting client state that cannot live in component state.
- Keep forms, hooks, and UI mappings type-safe and implementation-focused.
- Default to Server Components; add
- Define testing and release expectations:
- Backend TDD loop: Red -> Green -> Refactor.
- Unit tests stay DB-free; integration and E2E tests run in parallel with dynamic IDs.
- Release in small, reversible steps with a rollback plan.
Adoption workflow (existing codebase)
- Map current architecture and pain points.
- Select the smallest set of changes that enforce clear module boundaries.
- Migrate one vertical slice first.
- Add guardrails (lint/type/test checks) to prevent regression.
- Roll out module-by-module.
Stack defaults
Use references/stack-defaults.md as the default baseline. Deviate only when constraints require it.
Validation loop
Run this loop before finalizing architecture decisions:
- Verify consistency:
- Naming, module boundaries, and middleware rules are applied the same way across services.
- Verify quality gates:
npm run lintnpm run check-typesnpm run test --workspace=<pkg>(or equivalent targeted tests)
- Verify operability:
- Observability, health checks, and rollback path are defined.
- If any check fails:
- Fix the architecture brief or conventions.
- Re-run the loop.
Output template
Use this structure for architecture recommendations:
# Architecture brief
## Context and constraints
## Repo shape
## Backend module contracts
## Request context and middleware policy
## Frontend boundaries
## Testing strategy
## Rollout and rollback plan
## Open risks and follow-ups
Skill handoffs
- Use
ui-auditfor final UI quality checks. - Use
ui-animationfor motion-specific guidance.
Conventions
- Prefer
interfaceovertypefor object contracts. - Use
import typefor types. - Keep formatting consistent (2-space indentation, double quotes, semicolons, 100-character line width).