본문으로 건너뛰기
cccskills
무료GitHub에 공개

architecture

Clean Architecture (Uncle Bob) guide for this project. Use when creating modules, entities, use cases, presenters, controllers, gateways, guards, services. Contains tactical patterns and structure conventions.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(3)

  • SKILL.md8.7 KB
  • examples.md15.4 KB
  • references.md3.3 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Clean Architecture - Tactical Guide

Persona

Read .claude/roles/architect.md — adopt this profile and follow all its rules.

Activation

This skill activates for any creation or modification of architectural components:

  • Entities, Use Cases, Presenters
  • Controllers, Gateways, Guards
  • Services
  • Module structure

Core Principle

"The architecture should scream the intent of the system." — Uncle Bob

┌─────────────────────────────────────┐
│       Interface Adapters            │  ← Controllers, Presenters, Gateways
├─────────────────────────────────────┤
│           Use Cases                 │  ← Application Business Rules
├─────────────────────────────────────┤
│            Entities                 │  ← Enterprise Business Rules
└─────────────────────────────────────┘

Dependency Rule: Dependencies point inward. The domain knows nothing about infrastructure.


Module Structure

src/
├── entities/
│   └── <entity>/
│       ├── <entity>.ts              # Entity + business logic
│       ├── <entity>.schema.ts       # Zod schema
│       ├── <entity>.guard.ts        # Boundary validation
│       └── <entity>.gateway.ts      # Interface (port)
├── usecases/
│   └── <action><Entity>.usecase.ts
├── interface-adapters/
│   ├── controllers/
│   │   ├── webhook/
│   │   │   └── <platform>.controller.ts
│   │   ├── http/
│   │   │   └── <feature>.routes.ts
│   │   └── mcp/
│   │       └── <action>.handler.ts
│   ├── presenters/
│   │   └── <feature>.presenter.ts
│   ├── gateways/
│   │   └── <entity>.<source>.gateway.ts
│   └── adapters/
│       └── <platform>.adapter.ts
├── shared/
│   └── foundation/
│       ├── usecase.base.ts
│       ├── guard.base.ts
│       ├── gateway.base.ts
│       └── presenter.base.ts
└── tests/
    ├── units/
    ├── factories/
    ├── stubs/
    └── mocks/

Components

Entity

Pure business logic, independent of any framework.

// entities/review/reviewScore.valueObject.ts
export class ReviewScore {
  private constructor(private readonly props: ReviewScoreProps) {}

  static create(props: ReviewScoreProps): ReviewScore {
    const validated = ReviewScoreSchema.parse(props);
    return new ReviewScore(validated);
  }

  get blocking(): number {
    return this.props.blocking;
  }

  get severity(): Severity {
    if (this.blocking > 0) return 'critical';
    if (this.warnings > 0) return 'warning';
    if (this.suggestions > 0) return 'info';
    return 'clean';
  }

  get hasBlockingIssues(): boolean {
    return this.blocking > 0;
  }
}

Use Case

Orchestrates a business action. One use case = one user intention.

// usecases/triggerReview.usecase.ts
export function triggerReview(
  params: TriggerReviewParams,
  deps: TriggerReviewDependencies
): TriggerReviewResult {
  const { queuePort, reviewRequestTrackingGateway, logger } = deps;

  const jobId = queuePort.createJobId(params.platform, params.projectPath, params.reviewRequestNumber);

  if (queuePort.hasActiveJob(jobId)) {
    return { status: 'deduplicated', reason: 'Review already in progress' };
  }

  const enqueued = queuePort.enqueue(job);
  if (!enqueued) {
    return { status: 'failed', reason: 'Queue full or job rejected' };
  }

  reviewRequestTrackingGateway.recordPush(params.localPath, params.reviewRequestNumber, params.platform);
  return { status: 'success', jobId };
}

Presenter

Transforms business data into a ViewModel. Contains ALL presentation logic.

// interface-adapters/presenters/reviewList.presenter.ts
export class ReviewListPresenter {
  constructor(private jobPresenter: JobStatusPresenter) {}

  present(active: JobStatus[], recent: JobStatus[]): ReviewListViewModel {
    const activeJobs = active.map(job => this.jobPresenter.present(job));
    const recentJobs = recent.map(job => this.jobPresenter.present(job));
    const totalCount = activeJobs.length + recentJobs.length;

    return {
      activeJobs,
      recentJobs,
      totalCount,
      isEmpty: totalCount === 0,
      emptyMessage: 'No active reviews',
    };
  }
}

ViewModel

Simple data structure. Formatted strings, UI booleans. Defined with a TypeScript interface.

// interface-adapters/presenters/reviewList.presenter.ts
export interface ReviewListViewModel {
  activeJobs: JobStatusViewModel[];
  recentJobs: JobStatusViewModel[];
  activeCount: number;
  recentCount: number;
  totalCount: number;
  isEmpty: boolean;
  emptyMessage: string;
}

Controller

Orchestrates Use Case + Presenter. Entry point for an action.

// interface-adapters/controllers/webhook/github.controller.ts
export async function handleGitHubWebhook(
  request: FastifyRequest,
  reply: FastifyReply,
  logger: Logger,
  trackingGateway: ReviewRequestTrackingGateway,
  deps: GitHubWebhookDependencies
): Promise<void> {
  const { trackAssignment, recordCompletion } = deps;

  const verification = verifyGitHubSignature(request);
  if (!verification.valid) {
    reply.status(401).send({ error: verification.error });
    return;
  }

  // Orchestrate use cases...
}

Gateway

Interface (port) in entities, implementation in interface-adapters.

// entities/reviewContext/reviewContext.gateway.ts (CONTRACT)
export interface ReviewContextGateway {
  create(input: CreateReviewContextInput): CreateReviewContextResult;
  read(localPath: string, mergeRequestId: string): ReviewContext | null;
  exists(localPath: string, mergeRequestId: string): boolean;
  appendAction(localPath: string, mergeRequestId: string, action: ReviewContextAction): UpdateResult;
}

// interface-adapters/gateways/reviewContext.fileSystem.gateway.ts (IMPLEMENTATION)
export class ReviewContextFileSystemGateway implements ReviewContextGateway {
  read(localPath: string, mergeRequestId: string): ReviewContext | null {
    const filePath = this.getFilePath(localPath, mergeRequestId);
    if (!existsSync(filePath)) return null;
    const content = readFileSync(filePath, 'utf-8');
    return JSON.parse(content) as ReviewContext;
  }
}

Guard

Boundary validation with Zod.

// entities/reviewContext/reviewContextAction.guard.ts
export const reviewContextActionGuard = createGuard(reviewContextActionSchema);

export function parseReviewContextAction(data: unknown): ReviewContextAction {
  return reviewContextActionGuard.parse(data);
}

export function isValidReviewContextAction(data: unknown): data is ReviewContextAction {
  return reviewContextActionGuard.safeParse(data).success;
}

export function parseReviewContextActions(data: unknown): ReviewContextAction[] {
  return reviewContextActionsGuard.parse(data);
}

Service

Utilities injected via Dependencies.

ScopeLocation
Cross-contextshared/services/
Specific to bounded contextinterface-adapters/gateways/ or services/

Dependency Injection

Via composition root in src/main/routes.ts. See references.md for details.

// main/routes.ts — the "dirty" component (Uncle Bob Ch. 26)
app.post('/webhooks/github', async (request, reply) => {
  await handleGitHubWebhook(request, reply, deps.logger, trackingGw, {
    reviewContextGateway: deps.reviewContextGateway,
    threadFetchGateway: new GitHubThreadFetchGateway(defaultGitHubExecutor),
    trackAssignment: new TrackAssignmentUseCase(trackingGw),
    recordCompletion: new RecordReviewCompletionUseCase(trackingGw),
  });
});

Test Doubles

tests/
├── stubs/
│   └── reviewContextGateway.stub.ts    # Happy path
├── factories/
│   └── reviewContext.factory.ts         # Test data creation
└── mocks/

See examples.md for concrete examples.


Anti-patterns to Avoid

  • No business logic in Controllers
  • No presentation logic in Use Cases
  • No infrastructure awareness in Entities
  • No outward-pointing dependencies
  • No as Type assertions — use guards with Zod

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Guide for creating optimized Claude Code agents. Use to design a new specialized agent, a swarm sub-agent, or a multi-agent workflow. Follows the Skill → Agent (preloaded skills) → Skill orchestration pattern.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DGouron/review-flow442026년 10월 21일 업데이트

Anti-overengineering guide based on YAGNI. Use before adding an architectural pattern, during code review, or to challenge an overly complex implementation.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DGouron/review-flow442026년 10월 21일 업데이트

Audit documentation for duplication, staleness, language issues, and verbosity. Read-only — produces an actionable report.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DGouron/review-flow442026년 10월 21일 업데이트

Local auto-review of modified code (no GitLab/GitHub). Runs 5 sequential audits and generates a local report. Ideal for validating code before creating a MR/PR.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DGouron/review-flow442026년 10월 21일 업데이트

Clean Code (Robert C. Martin) guide to produce and challenge code that is readable, durable, simple, and consistent. Covers naming, functions, comments, smells, and refactor heuristics. Complements `/solid` (SOLID principles) and `/anti-overengineering` (YAGNI/KISS). Activate during the REFACTOR phase of the TDD cycle and during implementation self-review.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DGouron/review-flow442026년 10월 21일 업데이트

commit

무료

Safe commit and push workflow. Creates a commit following conventions and pushes. Husky handles verifications (TypeScript, Biome, tests).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DGouron/review-flow442026년 10월 21일 업데이트

DGouron의 스킬 모두 보기

이 스킬의 문제 신고