usecase
Use Case Skill
Installation: If not already installed, add the package with pnpm add @efesto-cloud/usecase.
Use cases are the application layer's entry points in a hexagonal architecture. They orchestrate domain entities, repositories (persistence), and services (email, auth, storage, etc.) to execute a single well-defined business operation.
Each use case ships four artifacts:
- Interface —
useCase/{domain}/I{Name}.ts— the type contract - Implementation —
useCase/{domain}/impl/{Name}.ts— the class - Symbol —
di/Symbols.ts— a new symbol entry - Binding —
di/container.ts— a DI binding line
Read references/templates.md for ready-to-copy code templates.
Before You Start
Gather context if the user hasn't already provided it:
- What does it do? Understand the operation in plain language (create / update / delete / fetch / search / export…)
- Authentication: Does it require a logged-in actor? Which auth type? (operator only, business entity only, dual/both, public/no auth)
- Mutation vs read: Does it write data? If yes → needs
@withTransactionand@audit. If no → neither. - Existing repos/services: What persistence or services does it need? Are they already defined? If a new repo method is needed, use the
/persistenceskill first. - New entity needed? If the domain entity doesn't exist yet, use the
/entityskill first. - Return type: What does the caller get back on success, and which errors can it return?
Step 1 — Interface
Create useCase/{domain}/I{Name}.ts. See references/templates.md for
all four auth-variant examples (operator, business entity, dual, no auth).
Rules:
- The input is always
With{Auth}<YourPayload>. If no auth is required, the payload is a plain object. - The return type is always
Result<SuccessType, UnionOfErrors>. - Import DTOs (not raw entities) for the success type.
- Keep payload types inline for simplicity; extract named types only when they're large or reused elsewhere.
Then add a type-only re-export to useCase/{domain}/index.ts:
export type { default as IMyUseCase } from "./IMyUseCase.js";
If this is a brand-new domain, also export the index.ts from the package's server.ts:
export * from "./useCase/{domain}/index.js";
Step 2 — Implementation
Create useCase/{domain}/impl/{Name}.ts. See references/templates.md
for ready-to-copy implementations of CREATE, UPDATE, DELETE, GET, and SEARCH.
The shape depends on whether the use case mutates data:
Mutating (CREATE / UPDATE / DELETE / ADD / REMOVE / PUBLISH…)
Add @withTransaction and @audit decorators. Wrap the body with auth.flatRun(async () => { … }, input).
Read-only (GET / SEARCH / EXPORT…)
No @audit, no @withTransaction (unless the operation has side effects or needs a consistent snapshot).
Decorator order
When using both decorators, always put @withTransaction before (outer) @audit (inner):
@injectable()
@withTransaction<IFoo>()
@audit<IFoo>({ ... })
export default class Foo implements IFoo { ... }
Auth service API
All auth services share the same interface:
auth.flatRun(fn, input)— callsfn()inside an auth check;fnreturnsResult<S, F>→ returnsResult<S, NotLoggedError | F>auth.run(fn, input)— same butfnreturns a plain value (not a Result)auth.get(input)— returnsMaybe<Actor>— use when you need the actor object itself (e.g. to stamp an owner field)
Injecting application services
Not all use cases only need repos. Some also need application-level services (email, file storage, CSV export, job queues…). Inject them from Symbols.Service.* the same way as repos:
constructor(
@inject(Symbols.Repo.FooRepo) readonly repo: IFooRepo,
@inject(Symbols.Service.EmailService) readonly email: IEmailService,
@inject(Symbols.DomainService.OperatorAuthService) readonly auth: IOperatorAuthService,
) {}
Step 3 — Symbol
Add the new use case name to di/Symbols.ts in two places:
1. The union type for the domain:
type MyDomainUC =
| "CreateMyEntity"
| "GetMyEntity"
| "MyNewUseCaseName"; // ← add here
2. The runtime enum object for the domain:
const MyDomainUCEnum = {
CreateMyEntity: "CreateMyEntity",
GetMyEntity: "GetMyEntity",
MyNewUseCaseName: "MyNewUseCaseName", // ← add here
} as const;
The Symbols.UseCase.myDomain object is derived automatically by the convert() helper — no further changes needed in the Symbols const.
Step 4 — Binding
Add one line to di/container.ts. Find the block for the domain and append:
container.bind<IMyUseCase>(Symbols.UseCase.myDomain.MyNewUseCaseName).to(MyNewUseCaseName).inRequestScope();
Add the import at the top of container.ts alongside the other imports for that domain:
import MyNewUseCaseName from "~/useCase/myDomain/impl/MyNewUseCaseName.js";
Logic Placement Guide
See references/logic-guide.md for a full decision table and examples.
Rule of thumb: if the operation can be expressed purely in terms of the entity's own fields, push it to the entity. As soon as it needs to load or save something, it belongs in the use case.
Checklist
Before finishing, verify:
- Interface file created and re-exported (type-only) from
useCase/{domain}/index.ts - Implementation file created with
@injectable() -
@auditpresent for all mutating operations (CREATE / UPDATE / DELETE / ADD / REMOVE…) -
@withTransactionpresent on all mutating operations - Auth service injected and
flatRun/runused to wrap the body - All repo and service deps declared in constructor with
@inject(Symbols.…) - Symbol name added to both the union type and the enum object in
Symbols.ts - Binding added to
container.tswith the matching import -
server.tsexports the domainindex.ts(only if this is a brand-new domain) - If a new repo method was needed: used the
/persistenceskill (persistence) - If a new entity was needed: used the
/entityskill (entity)
Related Skills
/entity(entity) — Create or update domain entities and DTOs/persistence(persistence) — Add repository interfaces, implementations, mappers, and MongoDB document types
More from efesto-cloud/skills
persistence
>
18population
>
17type-enum-dict
|
17value-object
|
17entity
Create or modify domain entities using the @efesto-cloud/entity package. Use this skill whenever the user asks to add a new entity, update an existing entity, add properties or methods to an entity, or work on the entity/dto layer. Trigger when the user says things like "create a Foo entity", "add a field to Bar", "I need a new domain object", or "add entity X". Also trigger for DTO creation or modification.
16webapp-loader-action
>
11