Refactor to Clean Architecture
Description
This skill guides the refactoring of tightly coupled NestJS services into a modular Clean Architecture structure. It separates business logic from infrastructure and ensures Dependency Inversion using abstract gateways.
When to use
Use this skill when:
- •Converting a "God Class" service (e.g., a service doing DB, API calls, and Logic) into Use Cases.
- •You see external libraries (axios, prisma, openai) imported directly into business logic.
- •You need to fix a module to match the project's "Golden Standard" (like the
TagModule).
Instructions
- •
Analyze the Dependency Graph
- •Identify external dependencies (Database, 3rd party APIs, File System).
- •Identify business rules (validations, data transformations, flow control).
- •
Create Domain Layer (
src/modules/<name>/domain/)- •Entities: Create pure TS classes defining the data structure. No decorators (unless absolutely necessary), no DB logic.
- •Gateways (Interfaces): Define interfaces for every external interaction.
- •Naming:
I<Name>Repositoryfor DB,I<Name>Gatewayfor external services. - •CRITICAL: Create a unique symbol or string constant for DI (e.g.,
export const IScraperGateway = Symbol('IScraperGateway');).
- •Naming:
- •
Create Use Case Layer (
src/modules/<name>/use-cases/)- •Create one file per action (e.g.,
process-article.use-case.ts). - •Inject dependencies using the Interface Token:
@Inject(IScraperGateway) private readonly scraper: IScraperGateway. - •Constraint: NEVER import
PrismaService,Axios, orLLMServicehere. Only imports from../domainare allowed.
- •Create one file per action (e.g.,
- •
Create Infrastructure Layer (
src/modules/<name>/infra/)- •Create adapters that implement the Domain Interfaces.
- •Naming:
Prisma<Name>Repository,JinaScraperGateway,GeminiContentGenerator. - •Put all external libs here (Prisma, Axios, AI SDK).
- •
Wire the Module (
src/modules/<name>/<name>.module.ts)- •Configure the
providersarray usingprovide(token) anduseClass(implementation). - •Example:
typescript
{ provide: IScraperGateway, useClass: JinaScraperAdapter }
- •Configure the
Examples
Input:
"Refactor the SearchService which uses duck-duck-scrape directly."
Output Plan:
- •Create
domain/gateways/search.gateway.interface.ts(Definessearch(query: string): Promise<string[]>). - •Create
infra/adapters/duckduckgo-search.adapter.ts(Implements interface, importsduck-duck-scrape). - •Update
use-casesto depend onISearchGateway. - •Update
module.tsto bindISearchGatewaytoDuckDuckGoSearchAdapter.