Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Use Case Reference

ASecurity

Reference implementation for backend use cases — error handling, structure, and patterns. MUST be loaded when creating or modifying any *.use-case.ts file.

33 stars
0 votes
0 copies
0 views
Added 9/20/2026
businesstypescriptgodatabasebackend

Works with

cli

Security Analysis

A100/100

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add ayunis-core/ayunis-core --skill use-case-reference --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Use Case Reference?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Use Case Reference
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ayunis-core-use-case-reference/badge)](https://www.skillsdirectory.com/skills/ayunis-core-use-case-reference)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: use-case-reference
description: "Reference implementation for backend use cases — error handling, structure, and patterns. MUST be loaded when creating or modifying any *.use-case.ts file."
---

# Use Case Reference Implementation

This skill defines the canonical use case structure. Every use case MUST follow this pattern.

The structural rules (error-boundary decorator, error wrapping, single `execute()` method, repository injection via interface) are non-negotiable. Things that **vary by project** — exact import paths, auth context handling, domain model style — are marked with `// ...` placeholders and inline comments.

## Structure

```typescript
import { Injectable, Logger } from '@nestjs/common';
// Error-boundary decorator — exact import path varies by project
import { HandleUnexpectedErrors } from 'src/common/decorators/handle-unexpected-errors.decorator';
// Module-specific errors
import { EntityNotFoundError, UnexpectedEntityError } from '../../entity.errors';
// Repository class — the type doubles as the DI token (no @Inject() needed)
import { EntityRepository } from '...';

interface DoSomethingCommand {
  entityId: string;
  // ... other command fields
}

@Injectable()
export class DoSomethingUseCase {
  private readonly logger = new Logger(DoSomethingUseCase.name);

  constructor(
    private readonly entityRepository: EntityRepository,
    // ... other dependencies (other use cases, application services, ports)
  ) {}

  // Error handling — REQUIRED on every execute(), see Rule 1
  @HandleUnexpectedErrors(UnexpectedEntityError)
  async execute(command: DoSomethingCommand): Promise<Entity> {
    this.logger.log({ entityId: command.entityId }, 'Doing something');

    // 1. Auth context — handling varies by project, see Rule 6 below

    // 2. Precondition checks (existence, permissions, business rules)
    // (multi-tenant projects typically pass userId here for tenant isolation)
    const entity = await this.entityRepository.findOne(command.entityId);
    if (!entity) {
      throw new EntityNotFoundError(command.entityId);
    }

    // 3. Business logic — mutate, orchestrate, call other use cases / repos
    //    (style varies: rich domain methods like entity.updateName(...), or
    //    anemic record updates — follow your project's convention)

    // 4. Persist and return
    return await this.entityRepository.save(entity);
  }
}
```

## Rules

### 1. Every `execute()` method MUST be decorated with `@HandleUnexpectedErrors`

```typescript
@HandleUnexpectedErrors(UnexpectedEntityError)
async execute(command: DoSomethingCommand): Promise<Entity> {
```

The decorator (from `src/common/decorators/handle-unexpected-errors.decorator.ts` — exact path varies by project) is the use case's error boundary:

- **Re-throws `ApplicationError`** subclasses as-is — these are domain errors with proper status codes
- **Logs unexpected errors** under the use-case class name — no extra context needed, the class name already describes the operation
- **Wraps unexpected errors in the module-specific `Unexpected*Error`** — never let raw errors escape

Do NOT hand-write try/catch error boundaries in `execute()`. A try/catch inside the business logic is fine only when the use case genuinely handles a failure (fallback, retry) rather than translating it.

### 2. Never throw HTTP exceptions from use cases

```typescript
// WRONG ✗ — couples domain to HTTP
throw new UnauthorizedException('User not authenticated');
throw new NotFoundException('Entity not found');

// CORRECT ✓ — domain errors
throw new UnauthorizedAccessError();
throw new EntityNotFoundError(entityId);
```

Use cases throw `ApplicationError` subclasses. The global exception filter converts them to HTTP responses.

### 3. One operation per file, one `execute()` per use case

A use case is a single business operation. Don't bundle multiple operations into one class with `execute1()` / `execute2()`. If you need two operations, write two use cases.

### 4. Extract broader responsibilities into application services

A use case represents one operation; it must not become the home for a broader capability. Extract cohesive policy, coordination, caching, batching, throttling, lifecycle management, or other independently testable and reusable behavior into a dedicated injectable service in the module.

Place reusable application behavior in `application/services/` and inject that service into each consuming use case. If the behavior is a technical mechanism tied to infrastructure, define an application port and keep the concrete service in infrastructure; use cases must not import concrete adapters.

Extract the responsibility when it has its own reason to change, owns state or lifecycle, can be named and tested independently, or could serve more than one operation. Keep code inline only when it is inseparable from that single use case.

```typescript
constructor(
  private readonly policyService: EntityPolicyService,
  private readonly externalCapability: ExternalCapabilityPort,
) {}
```

### 5. Inject repositories via the repository class — never database clients directly

Use cases depend on a repository class. They MUST NOT import a database client (TypeORM, Drizzle, raw `pg`, etc.) directly. This keeps the use case testable without a real database.

```ts
constructor(
  private readonly entityRepository: EntityRepository,
) {}
```

Whether `EntityRepository` is an **abstract class** with a separate concrete implementation bound via `{ provide: EntityRepository, useClass: ConcreteRepository }` (port/adapter pattern), or a **single concrete class** registered directly in `providers: [EntityRepository]`, is a project-level decision — see your project's structural conventions skill. In both cases the use case constructor looks the same: TypeScript reflection picks up the class as the DI token, so **no `@Inject()` decorator is needed**.

### 6. Auth context — varies by project

Auth handling is a project-level convention. Common patterns:

- **`ContextService` / async-local-storage**: read the current user from a request-scoped context service

  ```typescript
  const userId = this.contextService.get('userId');
  if (!userId) throw new UnauthorizedAccessError();
  ```

- **Command parameter**: the controller (or a guard) injects `userId` into the command before calling the use case
- **Guard + decorator**: an auth guard runs before the controller and rejects unauthenticated requests; use cases assume auth has already passed

Whichever pattern your project uses, apply it consistently inside `execute()`. **Never** accept `userId` ad-hoc in some use cases and not others.

### 7. Validate preconditions before mutating

Always check existence and permissions before performing writes:

```typescript
// (multi-tenant projects typically pass userId for tenant isolation)
const entity = await this.repository.findOne(id);
if (!entity) {
  throw new EntityNotFoundError(id);
}
// Only then proceed with mutation
```

### 8. Each module has its own errors file

Errors live in a module-specific errors file (e.g. `application/<module>.errors.ts`):

```typescript
// ApplicationError import path varies by project
import { ApplicationError } from '...';

export enum EntityErrorCode {
  ENTITY_NOT_FOUND = 'ENTITY_NOT_FOUND',
  UNEXPECTED_ENTITY_ERROR = 'UNEXPECTED_ENTITY_ERROR',
  // ... other codes
}

export abstract class EntityError extends ApplicationError {
  constructor(message: string, code: EntityErrorCode, statusCode: number = 400) {
    super(message, code, statusCode);
  }
}

export class EntityNotFoundError extends EntityError {
  constructor(entityId: string) {
    super(`Entity with ID ${entityId} not found`, EntityErrorCode.ENTITY_NOT_FOUND, 404);
  }
}

export class UnexpectedEntityError extends EntityError {
  constructor(error: unknown) {
    // If your project's `ApplicationError` accepts a metadata object as a 4th
    // arg (some do, some don't), pass `{ error }` for context.
    super('Unexpected error occurred', EntityErrorCode.UNEXPECTED_ENTITY_ERROR, 500);
  }
}
```

Every module MUST have an `Unexpected*Error` class for the `@HandleUnexpectedErrors` decorator.

### 9. Logger — use the class name, log entry, metadata first

```typescript
private readonly logger = new Logger(MyUseCase.name);

// At the start of execute():
this.logger.log({ relevantId: command.id }, 'Descriptive action');
```

Metadata goes in the **first** argument. Nest's logger treats the last argument
as the context, so `logger.log('Descriptive action', { relevantId })` silently
drops the object instead of emitting structured fields.

Unexpected-error logging is handled by the `@HandleUnexpectedErrors` decorator — do not add your own error logging for the boundary.

## Checklist

When creating or modifying a use case, verify:

- [ ] `execute()` is decorated with `@HandleUnexpectedErrors(Unexpected*Error)`
- [ ] No hand-written try/catch error boundary in `execute()`
- [ ] No HTTP exceptions (`NotFoundException`, `UnauthorizedException`, etc.)
- [ ] Auth context handled per the project's convention (Rule 6)
- [ ] Preconditions checked before mutations (entity exists, permissions valid)
- [ ] Repositories injected via DI token (interface), not concrete class
- [ ] Broader reusable responsibilities are delegated to application services or ports
- [ ] Use cases do not import concrete infrastructure services or adapters
- [ ] Module has an `Unexpected*Error` class in its errors file
- [ ] Logger uses class name, logs entry point with metadata first (error logging is the decorator's job)

Attribution

ayunis-coreayunis-core
View sourceMore from ayunis-core →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Solution Architect

Designs system architecture, component specifications, and technical integration strategy. Use when: designing solutions, system architecture, technology stack, or integration approaches.

192 votes

Akorchak:Venture Assessment

Generate a comprehensive VC investment assessment report for a company

72 votes

Stock Analysis

Analyze stocks and cryptocurrencies using Yahoo Finance data. Supports portfolio management (create, add, remove assets), crypto analysis (Top 20 by market cap), and periodic performance reports (daily/weekly/monthly/quarterly/yearly). 8 analysis dimensions for stocks, 3 for crypto. Use for stock analysis, portfolio tracking, earnings reactions, or crypto monitoring.

6511 votes

Just Fucking Cancel

Find and cancel unwanted subscriptions by analyzing bank transactions. Detects recurring charges, calculates annual waste, and helps you cancel with direct URLs and browser automation. Use when: 'cancel subscriptions', 'audit subscriptions', 'find recurring charges', 'what am I paying for', 'save money', 'subscription cleanup', 'stop wasting money'. Supports CSV import (Apple Card, Chase, Amex, Citi, Bank of America, Capital One, Mint, Copilot) OR Plaid API for automatic transaction pull. Out...

6511 votes

Telegram Compose

Compose rich, readable Telegram messages using HTML formatting via direct Telegram API. Use when: (1) Sending any Telegram message beyond a simple one-line reply, (2) Creating structured messages with sections, lists, or status updates, (3) Need formatting unavailable via Clawdbot's Markdown conversion (underline, spoilers, expandable blockquotes, user mentions by ID), (4) Sending alerts, reports, summaries, or notifications to Telegram, (5) Want professional, scannable message formatting wit...

6511 votes
View all in business →