1. Recommended SaaS-Style Structure Example
src
├── app/ # Next.js routing layer
│ ├── (auth)
│ │ ├── login
│ │ │ ├── page.tsx
│ │ │ └── _components
│ │ └── register
│ │ └── page.tsx
│ │
│ ├── (dashboard)
│ │ ├── layout.tsx
│ │ ├── inspections
│ │ │ ├── page.tsx
│ │ │ └── new
│ │ │ └── page.tsx
│ │ │
│ │ ├── vessels
│ │ │ └── page.tsx
│ │ │
│ │ └── reports
│ │ └── page.tsx
│ │
│ ├── api # Optional route handlers
│ │
│ ├── layout.tsx
│ └── page.tsx
│
├── features/ # Domain modules
│ ├── auth
│ │ ├── components
│ │ ├── actions
│ │ ├── services
│ │ └── types.ts
│ │
│ ├── inspections
│ │ ├── components
│ │ ├── actions
│ │ ├── services
│ │ └── types.ts
│ │
│ ├── vessels
│ │ ├── components
│ │ ├── actions
│ │ ├── services
│ │ └── types.ts
│ │
│ └── questionnaires
│ ├── components
│ ├── actions
│ ├── services
│ └── types.ts
│
├── components/ # Design system
│ ├── ui
│ └── common
│
├── hooks
│
├── lib # infrastructure
│ ├── prisma.ts
│ ├── db.ts
│ └── auth.ts
│
├── types # global shared types
│
└── config
└── constants.ts2. Architectural Philosophy
1️ App Router = navigation layer
app/Handles:
- routing
- layouts
- page composition
Example:
app/(dashboard)/inspections/page.tsxJust renders UI and calls feature logic.
2 Features = business logic
features/inspectionsContains:
components
actions
services
typesExample:
features/inspections/actions/createInspection.tsServer Action example:
"use server"
import { prisma } from "@/lib/prisma"
export async function createInspection(data) {
return prisma.inspection.create({
data
})
}3 Services = domain logic
Example:
features/inspections/services/inspectionService.tsimport { prisma } from "@/lib/prisma"
export async function getInspections() {
return prisma.inspection.findMany()
}4 Components
Reusable UI specific to the feature.
features/inspections/components/InspectionTable.tsx5 Shared UI
components/uiDesign system components:
Button
Input
Modal
Badge
Table3. Example Flow
User opens:
/inspectionsRoute:
app/(dashboard)/inspections/page.tsxPage calls:
features/inspections/services/getInspections()UI renders:
features/inspections/components/InspectionTable4. Why this works
Your typical apps involve:
- inspections
- vessels
- questionnaires
- reports
- users
Each becomes a feature module.
Example:
features/
vessels/
inspections/
questionnaires/
reports/
users/This avoids huge messy folders like:
lib/
services/
utils/5. Works perfectly with Prisma
Example infrastructure layer:
lib/prisma.tsimport { PrismaClient } from "@prisma/client"
export const prisma = new PrismaClient()6. Optional: API vs Server Actions
Modern Next.js apps increasingly use:
Server Actionsinstead of
API routesExample:
features/inspections/actions/createInspection.tsCalled directly from a form.
7. Real Example for Inspection App
features/
vessels/
inspections/
questionnaires/
responses/
reports/
users/This matches exactly your vetting / questionnaire architecture.
Result:
- scalable
- domain-oriented
- easy to maintain
- excellent with Prisma