vibecode.wiki
RU EN
~/wiki / server-i-logika / chistaya-arhitectura

Clean architecture: how not to turn a project into spaghetti

◷ 7 min read 2/19/2026

Next step

Open the bot or continue inside this section.

$ cd section/ $ open @mmorecil_bot

Article -> plan in AI

Paste this article URL into any AI and get an implementation plan for your project.

Read this article: https://vibecode.morecil.ru/en/server-i-logika/chistaya-arhitectura/ Work in my current project context. Create an implementation plan for this stack: 1) what to change 2) which files to edit 3) risks and typical mistakes 4) how to verify everything works If there are options, provide "quick" and "production-ready".
How to use
  1. Copy this prompt and send it to your AI chat.
  2. Attach your project or open the repository folder in the AI tool.
  3. Ask for file-level changes, risks, and a quick verification checklist.

If you've been wibcoding for a couple of months, you've probably come across this classic picture:

You start a simple pet project – “Come on, just a couple of endpoints.” After a week, you have 12 files in one src/ folder, 300-line functions, imports through 5 folders up, and when you try to add a new feature, the AI gives you “I don’t understand the context of the whole project.”.

As a result, the code turns into spaghetti, you spend hours refactoring instead of moving on, and you just want to delete the project and start over.

I've been through this 15 times. And then I started using clean architecture and everything changed. Now even large applications (payment bots, SaaS-tools, internal services) remain readable, easy to scale and – most importantly – the AI knows where to write.

Today we’re going to go through the shelves of what it is, why it’s a must-have for a Vibcoder in 2026, and how to get Claude/Cursor/Codex to generate code directly from a pure architecture.

What is pure architecture in simple words

Imagine the house:

  • The core of the house (Entities / Domain) - walls, foundations, bearing structures. These are business rules that never change (user, order, payment).
  • **Use Cases/Application (rooms and furniture) These are specific actions: “create an order”, “send a notification”.
  • **Adapters/Interface - windows and doors. This is how code communicates with the outside world: controllers, repositories, external APIs.
  • ** Outside (Frameworks & Drivers) - roof, garden, fence Database, framework (FastAPI, Express), Telegram Bot API, etc.

**Dependency Rule: Dependency arrows go **in only. The outer layers know about the inner layers, but the inner layers never know about the outer layers.

So your domain doesn't know you have PostgreSQL or MongoDB. It just says, “I need a user repository,” and that’s it.

Why this is ideal for Vibcoders

  1. AI better understands the problem When you tell a model to write a sign-up function, it sees a clear structure and doesn’t sculpt everything into one file.

  2. Easy to add and change Do you want to switch from Supabase to Prisma? You only change the Infrastructure layer. Domain and Use Cases remain intact.

    • Tests are written by themselves** Use Cases are pure functions. They can be tested without running the server.
  3. **The project doesn't turn into a monolith Even if you’re doing a 3-file pet project right now, it’s going to grow to 50 in a month and you won’t drown.

  4. *Easy to connect new AI agents * Cline, OpenCode, Pencil – they all know where to put the new code.

Practical folder structure (my 2026 template)

This is what my typical project looks like:

code
src/
─ Domain / The kernel never changes
Entities/#User, Order, Payment
←  ─ Value-objects / Email, Money, UUID
← exceptions/ #DomainError, ValidationError
♥
- ─ Application / Business Logic
Use-cases/# CreateUser, ProcessOrder, SendNotification
# CreateUserDto, OrderResponseDto
← Ports / Interfaces of Repositories and External Services
♥
ет─ Infrastructure/# Specific implementations
Repositories (Prisma, Supabase)
←  ─ external/ # Telegram, Stripe, Email
Config/ #env, database connection
♥
ы ─ presentation/# APIs, controllers, bots
#FastAPI routers or Next.js API routes
♥ ─ telegram/ #handlers bot
♥ ─ webhooks/ #Stripe webhooks etc.
♥
Utilities, guards, decorators
Main.ts / app.py # Entry Point

How to get AI to write in pure architecture (ready-made prompts)

Prompt No. 1 – creating a new project from scratch

text
You're an expert in pure architecture.
Create a project structure in TypeScript + FastAPI (or Next.js App Router) using Clean Architecture.

Layers:
domain (entities, value objects, exceptions)
- application (use-cases, dto, ports)
- infrastructure (persistence, external services)
- presentation (controllers/handlers)

Task: [Describe your task, for example, bot-shop with payment through UKassa]

First, output the full folder structure.
Then create all the necessary files with stubs.
In each use case, add comments with Dependency Inversion.

Prompt #2 - Refactoring an existing spaghetti code

text
I have an all-in-one-file project. Translate it to pure architecture.

Here's the current structure: [insert tree]

Keep all the logic, but divide into layers:
domain, application, infrastructure, presentation

First, show the new folder structure, then rewrite each layer in stages.

Example: Use Case “Create an Order”

domain/entities/order.ts

ts
export class Order {
  constructor(
    public readonly id: string,
    public readonly userId: string,
    public readonly items: OrderItem[],
    public readonly total: Money,
    public readonly status: OrderStatus = 'pending'
  ) {}

  confirm() {
    if (this.status !== 'pending') throw new DomainError('Order already confirmed');
    // бизнес-правило
  }
}

application/use-cases/create-order.ts

ts
export class CreateOrderUseCase {
  constructor(
    private readonly orderRepo: OrderRepositoryPort,
    private readonly paymentService: PaymentPort
  ) {}

  async execute(dto: CreateOrderDto): Promise<OrderResponseDto> {
    const order = new Order(/* ... */);
    await this.orderRepo.save(order);
    await this.paymentService.createPayment(order);
    return OrderMapper.toResponse(order);
  }
}

infrastructure/persistence/prisma-order-repo.ts

ts
export class PrismaOrderRepository implements OrderRepositoryPort {
  async save(order: Order) { /* prisma magic */ }
}

See? Use Case doesn't know about Prisma. He only knows about the OrderRepositoryPort interface.

How to implement gradually (even in an old project)

  1. Start with one Use Case (the most important one).
  2. Entities and Value Objects.
  3. Create Ports (interfaces).
  4. Implement them in Infrastructure.
  5. Connect via Dependency Injection (FastAPI – Depends, NestJS – @Injectable).

After 2-3 such Use Cases, the whole project will begin to ask for a pure architecture.

Frequent Vibcoder Mistakes and How to Avoid Them

  • **Import of the domain to the controller **
  • Business logic in the repository → repository should only save / read
  • Direct calls to external services from Use Case
  • Big Use Cases → One Use Case = One Action

Bottom line: pure architecture is not about “rightness”, but about speed and calmness

When the project is built correctly, you stop being afraid to add new features. AI is starting to work like a real senior developer because it understands the boundaries of responsibility. And you're like an architect who just gives you tasks.

I start every new project like this. Even if it's a "simple 100-line bot," in a week it'll still grow, and the structure will save me from rewriting.