Application architecture
Next.js tells you how routes work. It does not require every domain rule, database query, and UI component to live beside page.tsx.
Keep routing thin enough to see the application
src/
├─ app/
│ ├─ (marketing)/
│ ├─ dashboard/
│ ├─ api/
│ └─ layout.tsx
├─ features/
│ ├─ projects/
│ │ ├─ components/
│ │ ├─ actions.ts
│ │ ├─ queries.ts
│ │ └─ schema.ts
│ └─ users/
├─ components/
│ └─ ui/
├─ lib/
│ ├─ auth/
│ ├─ db/
│ └─ utils/
└─ types/This is a convention, not a Next.js requirement
Small applications do not need a large architecture diagram. Colocation is often clearer until a feature grows enough to deserve its own boundary.
The goal is to make ownership obvious, not to create folders for every noun.
Separate transport from domain work
A Server Action, Route Handler, and Server Component may all need the same business operation. Put the reusable domain or data logic below those entry points rather than duplicating it in each transport.
That also makes testing easier: the core rule can be tested without pretending every call came through HTTP.
Make server-only assumptions explicit
Database clients, secret-bearing SDKs, and privileged service functions should stay in modules that are clearly server-side. Do not rely on everyone remembering which utility is safe to import into a Client Component.
Clear module boundaries are cheaper than debugging an accidental client bundle leak later.
Can you explain “Application architecture” without reaching for the API reference?
Describe the main trade-off, one failure mode, and the decision you would make in a real project.