- Rename docs/decisions/adr-012-lazar-conformance.md → adr-012-feature-conventions.md - Strip "Lazar", "Plan 8/9/10/11", "refactor-logs" refs from all ADRs, architecture docs, HTML explainers, and feature/core AGENTS.md files - Update all incoming links in docs/, packages/*/AGENTS.md, HTML explainers Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.0 KiB
AGENTS.md — blog
Articles collection + content use cases (get articles, get article by slug, create article). Provides the Articles Payload collection and tRPC procedures for content management and retrieval.
Overview
@repo/blog owns: Article domain model, blog-scoped errors, the IArticlesRepository interface, three use cases, three controllers, a real Payload-backed repository, and the tRPC blogRouter. Query builders live in ./ui.
Layer responsibilities
| Layer | Key files |
|---|---|
| entities/models | article.ts — Zod schema + Article, ArticleStatus types |
| entities/errors | article.ts (ArticleNotFoundError), common.ts (InputParseError) |
| application/use-cases | get-articles.use-case.ts, create-article.use-case.ts, get-article-by-slug.use-case.ts — factory functions + exported schemas |
| application/repositories | articles.repository.interface.ts — IArticlesRepository |
| infrastructure/repositories | articles.repository.ts (real Payload-backed), articles.repository.mock.ts (in-memory) |
| interface-adapters/controllers | get-articles.controller.ts, create-article.controller.ts, get-article-by-slug.controller.ts — one file per use case |
| di | symbols.ts (BLOG_SYMBOLS), module.ts, container.ts, bind-production.ts |
| integrations/api | procedures.ts (blogProcedure), router.ts (blogRouter) |
| integrations/cms | collections/articles.ts — Payload Articles CollectionConfig |
| ui | src/ui/index.ts — re-exports articleBySlugQuery and listArticlesQuery |
Public exports
| Subpath | Contents |
|---|---|
. |
Article, ArticleStatus types; ArticleNotFoundError, InputParseError; all use-case schemas + input/output types + IXUseCase aliases; IXController type aliases; BlogRouter type |
./ui |
articleBySlugQuery, listArticlesQuery — React Query option builders |
./api |
blogRouter (tRPC router) |
./cms |
Payload Articles collection definition |
./di/bind-production |
bindProductionBlog(ctx: BindProductionContext) — swaps mock impls for real Payload-backed ones at app boot |
./di/bind-dev-seed |
bindDevSeedBlog(ctx: BindContext) — replaces the default empty mock with a populated one for dev / Storybook |
Use-case + controller patterns
See CLAUDE.md Key Conventions and docs/architecture/overview.md for the canonical factory templates.
Use cases
| Use case | Input schema | Output schema | Notes |
|---|---|---|---|
getArticlesUseCase |
getArticlesInputSchema — { status? } (status narrowed to articleStatusSchema) |
getArticlesOutputSchema — z.array(articleSchema) |
Returns all articles (optionally filtered) |
createArticleUseCase |
createArticleInputSchema — { title, slug, content, ... } |
createArticleOutputSchema — articleSchema |
Creates and persists an article |
getArticleBySlugUseCase |
getArticleBySlugInputSchema — { slug } |
getArticleBySlugOutputSchema — articleSchema |
Throws ArticleNotFoundError when slug not found |
Controllers
All three controllers use identity presenters — function presenter(value: XOutput) { return value; } — and return Promise<ReturnType<typeof presenter>>. All accept unknown input and safeParse with the use-case's xInputSchema, throwing InputParseError on failure.
Errors → tRPC codes
| Error class | tRPC code | Thrown by |
|---|---|---|
InputParseError |
BAD_REQUEST |
controllers (safeParse failure) |
ArticleNotFoundError |
NOT_FOUND |
getArticleBySlugUseCase |
Defined in src/integrations/api/procedures.ts via blogProcedure = t.procedure.use(defineErrorMiddleware([...])).
Tests
- Factories:
src/__factories__/article.factory.ts - Contract suite:
src/__contracts__/articles-repository.contract.ts— runs against mock and realArticlesRepository - Unit tests: colocated
*.test.tsnext to each source file - Feature integration:
tests/articles.feature.test.ts— full slice: tRPC caller → controller → use case → mock repo - R25 (output validation): each of the three use-case test files has a test that injects a malformed repository mock and asserts
.rejects.toBeInstanceOf(ZodError). - R26 (router error mapping):
router.test.tshasNOT_FOUNDonarticleBySlugwith unknown slug, andBAD_REQUESTon empty input. - R27/R28 (presenter shape): all three controllers use identity presenters — no reshape test obligation; the returned value equals the use-case output.
pnpm test --filter @repo/blog
pnpm test --filter @repo/blog -- --watch
See docs/guides/tdd-workflow.md for the full cycle.
Directory structure
src/
entities/
models/
article.ts
errors/
article.ts # ArticleNotFoundError
common.ts # InputParseError
application/
repositories/
articles.repository.interface.ts
use-cases/
get-articles.use-case.ts
create-article.use-case.ts
get-article-by-slug.use-case.ts
infrastructure/
repositories/
articles.repository.ts # real Payload-backed
articles.repository.mock.ts
interface-adapters/
controllers/
get-articles.controller.ts
create-article.controller.ts
get-article-by-slug.controller.ts
integrations/
api/
procedures.ts # blogProcedure
router.ts # blogRouter
cms/
collections/
articles.ts
index.ts
di/
symbols.ts # BLOG_SYMBOLS
module.ts
container.ts
bind-production.ts
ui/
index.ts # articleBySlugQuery, listArticlesQuery
query.ts
index.ts
__factories__/
article.factory.ts
__contracts__/
articles-repository.contract.ts
tests/
articles.feature.test.ts
What it must NOT import
- Any other feature package (
@repo/auth,@repo/media, etc.) - Any app package
@repo/core-api,@repo/core-cms,@repo/core-trpc,@repo/core-uidirectly; only@repo/core-sharedNote:
@repo/core-trpcand@repo/core-uiare optional packages scaffolded viapnpm turbo gen core-package trpc/ui. If not present, these constraints still apply to any future installation.
Cross-links
- ADR-012 (
docs/decisions/adr-012-feature-conventions.md) — factory-style use cases, per-use-case controllers, file-naming conventions - ADR-013 (
docs/decisions/adr-013-input-output-unification.md) — schemas-in-use-case, presenter,./uisubpath, error middleware