service-architecture
Use when making or reviewing backend service-architecture decisions — service boundaries, consolidation vs. splitting, inter-service communication (IPC/direct/ queue), auth/authorization patterns acro
SKILL.mdskills/service-architecture/SKILL.md
GitHubService Architecture
If LOCAL.md sits beside this skill, read it first: it carries this install's project
conventions, which are standing directives and win over the generic defaults here.
Goal: make sound service-architecture decisions, surface optimizations and trade-offs, and apply already-decided patterns consistently. The main failure in this domain is inventing a parallel solution to a solved problem.
Workflow
- Search before you build. Grep ALL the repos — legacy and new services — for the capability first. It frequently already exists under another name, and a wrong "missing feature" guess in a plan costs more than the grep.
- Decide where the logic lives. One service owns each table's writes: route writes through the owner (IPC), read pre-joined read models directly — two writers on one table is how data diverges. Pick the cheapest channel that respects ownership (direct read model < owning-service IPC < queue/event). Prefer deriving a value from data that already holds the truth over adding a column + migration.
- Weigh consolidation vs. split. Modules merged into one service call each other in-process (DI); keep the interservice client only for genuinely external services — re-IPC-ing into yourself adds latency and a failure mode for nothing. Shared libs a frontend consumes stay free of server-framework decorators and dependencies.
- Auth at the boundary, re-derived at the service layer. One endpoint serving several caller types uses ONE composite, ordered, short-circuiting guard with per-route config — duplicated endpoints per audience drift apart. Default to deny; fail closed. The guard answers "may you touch this resource?"; the service still clamps what the caller may grant or change.
- Migrate incrementally. Move the client-facing surface (URL + auth + docs + tests) first, keep delegating to the legacy engine when reimplementing it is large or risky, and delete the old endpoint only after confirming nothing else uses it.
- Verify the wiring live. Guard/provider/module changes are invisible to unit tests (mocks never build the DI graph) — a real boot is the only proof the change resolves.
The patterns behind each step, with their caveats: general-principles.md.
Common failure modes
- Inventing a parallel stack for a solved problem — a new guard, filter shape or helper when the codebase already has one. Step 1 exists to prevent exactly this.
- Mutating an existing endpoint's auth/shape in place instead of adding a sibling version and migrating the consumer (Hyrum's Law: every observable behavior becomes a contract someone depends on).
- Swapping a guard without checking what it wrote to the request — handlers may read a field the old guard set; a pass/fail-only replacement silently opens an authz hole.
- An auth decorator without its guard is inert — metadata does nothing until a guard reads it, so the route is silently OPEN.
- Gating access but not authority — clamp grantable levels in the service.
- Fanning out one external API call per list row — keep a synced read model for sortable/filterable columns and refresh only the visible page on demand.
- Building a parallel admin UI when the same permission-gated screens can serve both audiences — then failing to verify the lower tier can't reach the privileged actions.
- Stampeding boot-time tasks — gate them (advisory lock + change detection) so N instances booting don't all write.
- Declaring DI wiring done on green unit tests — see step 6.
Related skills
- deprecation-and-migration: the mechanics of moving logic off a legacy service once this skill has decided where it lands.
- code-quality: the hard rules every backend diff meets — one responsibility per file, readable by a junior engineer, and one rhythm per kind of work (every endpoint moves data through the same stages).
Capturing learnings
Record architectural decisions, the reasoning, patterns adopted and anti-patterns to
avoid in LEARNINGS.md beside this skill, when your copy keeps one. Pull the general
principle out into the general-principles reference, and keep project-specific
conventions in LOCAL.md. Dedupe and merge; if any one file grows past ~250 lines,
split it further.