Appearance
Project Structure
Core Directories
There are 4 core directories we use in the project:
app- pages of the Next.js application and API routes. Keep there only files used once per page (or only reused in its sub-pages).modules- domain-related code. Create subdirectory for each business domain of the project. Examples:cart,session,product.services- functionality-related code. Create subdirectory for each functionality of the project. Examples:forms,i18n,navigation.common- generic code, not related to any particular page, domain, or functionality. Examples:hooks/use-interval,helpers/string,components/button.
Dependency Boundaries
To keep the project structure clean we use Dependency Cruiser to enforce the following boundaries:
appcan depend on everything.modulescan depend onmodules,services, andcommon.servicescan depend onservicesandcommon.commoncan depend only oncommon(there are exceptions for some services likestylesortesting).
Proxy Files™
There are many cases where we don't want to import the external package directly across the codebase:
- We compose its utils with our own under one utility file (e.g.
src/common/helpers/string.ts). - We enhance its original functionality or types with our own (e.g.
src/common/helpers/date.ts). - We use it only for a specific use case, and don't want it to be used standalone (e.g.
src/common/components-2/sanitized-content.tsx).
For all such cases we limit the usage of the library to specific files using dependency-cruiser.mjs. You may check the config from time to time to understand which packages are limited this way. However, if you forgot about some of them, Dependency Cruiser will let you know in the CI.