Ts ddd use case
Create, review, or guide use case (application service) implementation in a TypeScript + DDD codebase. Use when the request involves `*.usecase.ts` files under `apps/api/src/<bc>/application/usecases/`, `application/services/<name>.service.ts` orchestrators, `UseCase<IN,OUT>` from `@acme/shared`, NestJS `@Injectable()` + `@Inject(<REPO_TOKEN>)` wiring, `Result.ok` / `Result.fail` / `withFail` / `Result.combine`, repository-port orchestration (`findBySlug`, `save`, `listByStatus`), aggregate state-transitions (`publish`, `addSection`), enum-typed inputs (`CelebrationKind`, `CelebrationStatusEnum.DRAFT`), or use-case tests under `apps/api/test/<bc>/application/usecases/` using `InMemoryCelebrationRepository`.From its SKILL.md
npx -y skills add llodev/skills --skill ts-ddd-use-caseAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.5 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
TypeScript DDD Use Case
MANDATORY — READ ENTIRE FILE: Before any implementation step, read
references/use-case-pattern.md completely.
Do NOT load other DDD skills (entity, repository, dto, controller) unless explicitly requested.
A use case orchestrates one application intent. It is the only layer allowed to talk to repository ports, domain entities, and other application services in the same bounded context. It never throws for expected failures, never maps to HTTP, never validates VO invariants itself.
Before You Start
Answer these before touching code:
- Is this an IN→OUT verb (
create-*,publish-*,add-*,list-*)? →application/usecases/<verb>-<noun>.usecase.tsimplementingUseCase<IN, OUT>. - Is this a reusable orchestrator without a clean IN→OUT shape (allocator, coordinator, resolver)? →
application/services/<name>.service.tsexposing one or more domain methods, noUseCaseinterface. SeeSlugAllocatorfor the canonical pattern. - Command or query? Write/mutation → repository port + domain entity. Read/projection → query port (or
repo.listByXwhen the read still returns entities). - What fails? Which pre-conditions must hold? What does each
awaitreturn onisFailure? - Update or create? Update requires loading current state first (
findBySlug/findById) and mutating via a named domain method (publish,addSection,cloneWith). Never merge raw input blind. - Does the input carry a discriminator/status/kind? Type it with the enum-typed union from contracts (
CelebrationKind), neverstring. Pass enum members (CelebrationStatusEnum.DRAFT), never literals.
Dependency Decision Table
| Goal | Dependency type | Returns |
|---|---|---|
| Create a new aggregate | CelebrationRepository.save | Result<void> → return the entity |
| State transition on existing aggregate | findBySlug + save | Result<Celebration> |
| Mutate a nested entity (section, item) | findBySlug + save (or saveSection) | Result<Section> (the new sub-entity) |
| List by status / filter | listByStatus | Result<Celebration[]> |
| Allocate unique slug across retries | SlugAllocator (application service) | Result<Slug> |
| Existence check before write | findBySlug → Result.fail(NOT_FOUND) | Domain error code |
| Aggregate across multiple BCs (rare) | Cross-BC contract or domain event | Combined DTO |
Core Rules
- Implement
UseCase<IN, OUT>from@acme/shared:execute(input: IN): Promise<Result<OUT>>. - Decorate the class with
@Injectable()from@nestjs/common. Use cases are providers, registered in the BC module. - Inject repository ports via
@Inject(<TOKEN>)using the symbol token from@<bc>/domain/repositories(e.g.CELEBRATION_REPOSITORY). Never inject the concrete adapter class. - Type the constructor field with the port interface (
CelebrationRepository), not the Firestore/InMemory class. - Fail early: every
awaitis followed byif (X.isFailure) return X.withFail;. Domain error codes go throughResult.fail("CODE"). - Delegate every invariant to the entity: call
Entity.tryCreate(...),entity.publish(),entity.addSection(...), etc. The use case checksisFailureand forwards; it does not re-validate VOs. - For enum-backed fields (status / kind / palette / layout / provider) pass enum members from contracts. Literal strings are a bug even when they match — they break refactors and silent renames.
- Imports use workspace aliases:
@acme/shared,@acme/<bc>-contracts,@<bc>/domain/...,@<bc>/application/.... No relative paths across layers. - The barrel
apps/api/src/<bc>/application/usecases/index.tsre-exports every use case; update it the moment a new file lands. - Class name drops the
UseCasesuffix (CreateCelebration, notCreateCelebrationUseCase). The file extension.usecase.tsalready encodes the role; doubling it in the class name is noise. Constructor injection sites read better asprivate readonly create: CreateCelebration.
NEVER
- NEVER
throwinsideexecutefor expected failures. ReturnResult.fail(...)orresult.withFail. Throwing breaksresult-to-httpmapping and lets unhandled exceptions reach NestJS. - NEVER map errors to HTTP status codes here. That is the controller's job (
presentation/<bc>.controller.tsviaresult-to-http). - NEVER import from
@nestjs/commonother thanInject+Injectable. NoHttpException, noLogger(use injected logger if needed). No Firebase, no Zod, no DTO classes — those live inpresentation/andinfra/. - NEVER re-implement VO validation (slug shape, kind whitelist, palette lookup). Call
Celebration.tryCreate/Section.tryCreate; propagatewithFail. - NEVER inject the concrete repository (
FirestoreCelebrationRepository,InMemoryCelebrationRepository). Only the symbol token + port interface. - NEVER type a status/kind field as
string. UseCelebrationKind/CelebrationStatusfrom contracts. Never writestatus: "draft"; writestatus: CelebrationStatusEnum.DRAFT. - NEVER update without loading the aggregate first (
findBySlug). Partial merges destroy existing props. - NEVER put cross-cutting orchestration that doesn't fit IN→OUT into a use case. Promote it to
application/services/<name>.service.ts(e.g.SlugAllocator).
Result API — project-specific bits
result.withFail— getter that re-wraps a failedResult<A>asResult<B>without recomputing. Use it for everyif (x.isFailure) return x.withFail;step. Not the genericResultAPI — this is our shortcut.- Domain error codes are SCREAMING_SNAKE_CASE and catalogued in
apps/api/src/shared/http/error-codes.ts. Add new codes there before using them in a use case.
References
See references/use-case-pattern.md for: real file layout, the canonical create / state-transition / nested-mutation / list snippets, the application/services/ exception, the test strategy with InMemoryCelebrationRepository, enum-fixture rules, and the implementation checklist.
See examples/create-greeting.usecase.example.ts for a self-contained use case + fake repo + Jest test using enum members.
What ships with it: 9 files
53.2 KB alongside SKILL.md, 1 of them executable
docs/
- i18n/README.es-ES.md5.5 KB
- i18n/README.pt-BR.md5.4 KB
examples/
- create-greeting.usecase.example.tsruns9.0 KB
- README.md1002 B
references/
- use-case-pattern.md24.3 KB
- CHANGELOG.md1010 B
- LICENSE1.1 KB
- package.json788 B
- README.md5.1 KB
Gives 0 of the 12 instructions most test skills give in ~1.6k tokens
Counted across 1,201 of the 2,096 authors here whose files we hold, read 2026-09-06
- Write a failing test before writing codein 43 of 1201, across 36 files
- Run the full test suitein 36 of 1201, across 35 files
- Test only one variable per experimentin 34 of 1201, across 17 files
- Read product marketing context before asking questionsin 34 of 1201, across 14 files
- Mock external dependenciesin 34 of 1201, across 30 files
- Define primary, secondary, and guardrail metricsin 33 of 1201, across 16 files
- Pre-determine sample size before startingin 31 of 1201, across 14 files
- Test behavior rather than implementationin 31 of 1201, across 29 files
- Formulate a hypothesis before designing a testin 30 of 1201, across 13 files
- Document every test hypothesis, variant, and resultin 29 of 1201, across 11 files
- Use descriptive test function namesin 25 of 1201, across 21 files
- Commit to the methodology without stopping earlyin 24 of 1201, across 8 files
Said here and by no other author read
- Read the entity pattern reference before starting
- Implement UseCase interface from shared package
- Decorate use case classes with Injectable
- Inject repository ports using symbol tokens
- Type constructor fields with port interfaces
- Return Result.fail for expected failures
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.