Sử dụng với Next.js
FSD tương thích với Next.js trong cả phiên bản App Router và Pages Router nếu bạn giải quyết được xung đột chính — thư mục app và pages.
App Router
Phần tiêu đề “App Router”Xung đột giữa FSD và Next.js trong layer app
Phần tiêu đề “Xung đột giữa FSD và Next.js trong layer app”Next.js đề xuất sử dụng thư mục app để định nghĩa các route của ứng dụng. Nó mong đợi các file trong thư mục app tương ứng với các pathname. Cơ chế routing này không phù hợp với khái niệm FSD, vì không thể duy trì cấu trúc slice phẳng.
Giải pháp là di chuyển thư mục app của Next.js vào thư mục gốc của dự án và import các pages FSD từ src, nơi chứa các layer FSD, vào thư mục app của Next.js.
Bạn cũng cần thêm thư mục pages vào thư mục gốc của dự án, nếu không Next.js sẽ cố gắng sử dụng src/pages như Pages Router ngay cả khi bạn sử dụng App Router, điều này sẽ làm hỏng quá trình build. Cũng nên đặt file README.md bên trong thư mục pages gốc này để mô tả tại sao nó cần thiết, mặc dù nó trống.
Thư mụcapp App folder (Next.js)
Thư mụcapi
Thư mụcget-example
- route.ts
Thư mụcexample
- page.tsx
Thư mụcpages Empty pages folder (Next.js)
- README.md
Thư mụcsrc
Thư mụcapp
- api-routes API routes
Thư mụcpages
Thư mụcexample
- index.ts
Thư mụcui
- example.tsx
Thư mụcwidgets/
- …
Thư mụcfeatures/
- …
Thư mụcentities/
- …
Thư mụcshared/
- …
Ví dụ về việc re-export một page từ src/pages trong app của Next.js:
export { ExamplePage as default, metadata } from '@/pages/example';Middleware
Phần tiêu đề “Middleware”Nếu bạn sử dụng middleware trong dự án, nó phải được đặt ở thư mục gốc của dự án cùng với thư mục app và pages của Next.js.
Instrumentation
Phần tiêu đề “Instrumentation”File instrumentation.js cho phép bạn giám sát hiệu suất và hành vi của ứng dụng. Nếu bạn sử dụng nó, nó phải được đặt ở thư mục gốc của dự án, tương tự như middleware.js.
Pages Router
Phần tiêu đề “Pages Router”Xung đột giữa FSD và Next.js trong layer pages
Phần tiêu đề “Xung đột giữa FSD và Next.js trong layer pages”Các route nên được đặt trong thư mục pages ở thư mục gốc của dự án, tương tự như thư mục app cho App Router. Cấu trúc bên trong src nơi các thư mục layer được đặt vẫn không thay đổi.
Thư mụcpages Pages folder (Next.js)
- _app.tsx
Thư mụcapi
- example.ts API route re-export
Thư mụcexample
- index.tsx
Thư mụcsrc
Thư mụcapp
Thư mụccustom-app
- custom-app.tsx Custom App component
Thư mụcapi-routes
- get-example-data.ts API route
Thư mụcpages
Thư mụcexample
- index.ts
Thư mụcui
- example.tsx
Thư mụcwidgets/
- …
Thư mụcfeatures/
- …
Thư mụcentities/
- …
Thư mụcshared/
- …
Ví dụ về việc re-export một page từ src/pages trong pages của Next.js:
export { Example as default } from '@/pages/example';Custom _app component
Phần tiêu đề “Custom _app component”Bạn có thể đặt Custom App component của mình trong src/app/_app hoặc src/app/custom-app:
import type { AppProps } from 'next/app';
export const MyApp = ({ Component, pageProps }: AppProps) => { return ( <> <p>My Custom App component</p> <Component { ...pageProps } /> </> );};export { App as default } from '@/app/custom-app';Route Handlers (API routes)
Phần tiêu đề “Route Handlers (API routes)”Sử dụng segment api-routes trong layer app để làm việc với Route Handlers.
Hãy chú ý khi viết code backend trong cấu trúc FSD — FSD chủ yếu dành cho frontend, nghĩa là đó là điều mà mọi người sẽ mong đợi tìm thấy. Nếu bạn cần nhiều endpoint, hãy cân nhắc tách chúng thành một package khác trong một monorepo.
import { getExamplesList } from '@/shared/db';
export const getExampleData = () => { try { const examplesList = getExamplesList();
return Response.json({ examplesList }); } catch { return Response.json(null, { status: 500, statusText: 'Ouch, something went wrong', }); }};export { getExampleData as GET } from '@/app/api-routes';import type { NextApiRequest, NextApiResponse } from 'next';
const config = { api: { bodyParser: { sizeLimit: '1mb', }, }, maxDuration: 5,};
const handler = (req: NextApiRequest, res: NextApiResponse<ResponseData>) => { res.status(200).json({ message: 'Hello from FSD' });};
export const getExampleData = { config, handler } as const;export { getExampleData } from './get-example-data';import { getExampleData } from '@/app/api-routes';
export const config = getExampleData.config;export default getExampleData.handler;Các đề xuất bổ sung
Phần tiêu đề “Các đề xuất bổ sung”- Sử dụng segment
dbtrong layersharedđể mô tả các database query và việc sử dụng chúng ở các layer cao hơn. - Logic caching và revalidating queries tốt nhất nên được giữ cùng chỗ với các query.