3-Layer Architecture:How to Make AI Write Clean Code
The 3-layer architecture that stops AI from writing spaghetti: UI, business logic, and data — plus the exact prompt sequence that keeps every layer clean.
AI generates messy code because your project has no architecture, not because the AI is bad. A 3-layer architecture fixes it. Give it three clear layers — components for UI, services for business logic, a data layer for the database — and prompt one layer at a time, and the same AI produces clean, testable, scalable code. I built over 3M lines this way.
Here is the pattern, why it works, and the exact prompt sequence.
The problem: AI without structure creates chaos
You prompt AI to build a feature. It gives you code. You paste it in. A week later, your codebase is a mess: database calls scattered everywhere, business logic mixed with UI, copy-paste code all over, impossible to test, breaks when you try to scale.
The AI is not the problem. Your architecture is.
AI generates what you ask for. But without clear architecture, it mixes concerns, creates inconsistent patterns, and cannot maintain your codebase structure — every prompt creates new chaos. AI does not know your architecture. You must guide it.
The solution: a 3-layer architecture
Picture your app as a stack of three floors. Components on top — UI only. Services in the middle — business logic. The data layer at the bottom — database and APIs. Each floor only ever talks to the one directly beneath it. When AI knows which layer it is working on, it generates clean code.
- Layer 1 — Components (UI only). What belongs here: display logic, user interactions, routing, layout. What does not: database calls, business logic, validation, permissions. A bad component fetches users from the database inside itself, filtering by status along the way. A good component asks one hook for "active users", shows a loading skeleton while it waits, and renders the list. That is the entire job.
- Layer 2 — Services (business logic). What belongs here: data fetching, validation, permissions, business rules, data transforms. What does not: anything that renders, or component-specific state. A UserService knows the rules — an organisation is required, which filters apply, what happens on failure — and it is the only place those rules live.
- Layer 3 — Data (database and APIs). Database queries, API calls, external integrations. Accessed only through services, never directly from components.
How to prompt AI with this pattern
The wrong way is one prompt: "Build a user management feature." The result is everything in one component — database calls, validation, UI, all mixed. The right way is three prompts, bottom-up, one layer each:
- Service first: "Generate a UserService with methods to get, create, update, and delete users. Include validation and error handling."
- Hook second: "Create a useUsers hook that wraps the UserService with caching."
- Component last: "Build a UserList component that uses the useUsers hook. Display loading states and handle errors."
Result: clean, separated code, each layer focused on its responsibility. Prompting for everything at once is the number-one mistake — AI generates mixed code with all layers tangled. One layer at a time, build bottom-up.
The bookThe Solopreneur RevolutionLuis Gonçalves on building a solopreneur business with AI while employed.Get the book →Works with any stack
Same pattern, different tools:
| Stack | The 3-layer chain |
|---|---|
| React + Supabase | Component → Hook → Service → Supabase |
| Next.js + Prisma | Component → Server Action → Service → Prisma |
| Vue + Firebase | Component → Composable → Service → Firebase |
| Angular + REST API | Component → Service → HTTP Client → REST API |
Got messy code already? The 5-step migration
- Find all database calls. Search your components for direct database and API calls.
- Create services. Move all data access into service classes — the query that lived inside a component now lives in a service method with error handling.
- Create hooks. Wrap each service with your framework's data-fetching layer so components get caching and loading states for free.
- Update components. Replace the fetch-inside-the-component pattern with a single hook call.
- Let AI help. Prompt: "Refactor this component to use the hook pattern. Move database logic to a service." AI will migrate each piece.
The payoff shows up immediately in testing: services test without any UI, hooks test as thin wrappers, and components test with the hook mocked out. Clean layers = easy testing.
Why this works
Without layers, AI generates monolithic components, duplicates logic across files, and produces code that is hard to test, hard to maintain, and breaks when scaling. With layers, AI generates focused code for each layer, logic is centralised in services, each layer tests independently — and it scales to millions of lines.
I built 3M+ lines of code this way. It works. This is the exact architecture behind FIKR Space: 3M+ lines, 964 database tables, 5 integrated products, built with Claude, running in production. The two mistakes to avoid: skipping services ("I'll just use hooks directly with the database" — business logic in hooks is hard to test and impossible to reuse) and fat components ("I'll put validation in components" — you cannot reuse or test it without UI).
Before shipping, run the checklist: no database imports in components, all business logic in services, components only handle UI, hooks wrap services, services testable without UI, error handling in all layers. This pattern is pillar 2 of the five pillars of production software — and once the layers exist, structured prompts like the PRP method get dramatically more reliable, because every prompt has an obvious home.
12 weeksYour corporate career was never the planApply to the Solopreneur Accelerator.Apply now →Frequently asked questions
Do I need to understand code to use the 3-layer architecture?
You need to understand the map, not the streets. Knowing that UI, business logic, and data access are three separate floors — and that each prompt targets exactly one — is enough to direct AI correctly. The architecture is a communication tool between you and the AI, and directing is your half of the job.
Is 3 layers overkill for a small MVP?
No, because the cost is nearly zero and the alternative compounds. Three prompts instead of one on day 1 is minutes of extra work; untangling database calls from fifty components in month six is weeks. The pattern scales down to a weekend project and up to millions of lines — I have run it at both ends.
What goes in a service versus a hook?
Rules go in the service; plumbing goes in the hook. If it is a business decision — what counts as valid, who is allowed, how a value is calculated — it belongs in the service. If it is about delivering data to the screen — caching, loading flags, refetching — it belongs in the hook. When unsure, put it in the service: services are easier to test and reuse.
How do I make AI respect the layers automatically?
State the rule in your standing project instructions: components never import the database client, all data access goes through services, hooks wrap services. Then make every prompt name its layer ("create a service method for…"). AI follows the architecture that is actually written down — never the one in your head.
How trapped are you, really?
Take the Corporate Suffocation Index, the free solopreneur readiness assessment.