--- tags: Setup --- # Organizing Code Files in Your App For the best results, it’s recommended to start with an [initialized Next.js app](https://hackmd.io/t_ALNiZCRwS8b6kAm5r80Q), even though many of the concepts discussed here apply equally well to other contexts. ## Organize Features as the Primary Structure of Your Folders Many developers rely heavily on global search shortcuts to quickly find a file by its name, which they often memorize with surprising ease. Unfortunately, for some developers, including myself, this approach is challenging. The issue I face is that I can't quickly remember the exact file name, making it difficult to navigate efficiently. For people like me, the most comfortable method is to use the file hierarchy to navigate and reach the desired file. However, I’ve turned this into an unfair advantage, as it forced me over the years to design **folder structures that are extremely efficient**, providing me with much more than just the discoverability I initially needed. Maintaining code in an organized and pattern-based way reduces the number of questions about **where to create a new file**. It also **helps new developers** intuitively discover various parts of the application. Furthermore, isolating code into logical sections makes it **easier to refactor or restructure**. This structure also **facilitates parallel contributions**, minimizing potential conflicts between developers. Moreover, with clearly defined boundaries, it's easier to **isolate parts of the application**, making them candidates for migration into reusable libraries or other products. The **most effective global axis** of separation is **feature-based**, as it helps you stay focused on your task. You rarely work on multiple features simultaneously, so once you’ve identified the folder corresponding to the feature you’re working on, you’ve effectively eliminated all other features. This way, you know that all the code you may need to interact with is co-located in the same place. Create a `features` folder next to the `app` folder in `src`. ```bash mkdir src/features ``` Now create the feature you will working on: ```bash mkdir src/features/organization ``` ***Organizations** help users organize resources under a common set of members, making it easier to manage access control and ensure efficient collaboration within the team* ## Separate Domain, Presentation, and Transfer Logic A good secondary approach to splitting your code within a feature is to identify isolated layers. I recommend organizing by *domain*, **presentation** and **transfer**: - **domain** defines the core functionality of your application. It encompasses the business logic related to the problem you're solving for your users. All logic directly tied to the business domain should reside in the domain folder. - **presentation** is responsible for how your application communicates with the user. It focuses on providing the user interface, handling interactions, and is solely concerned with displaying information to the user. - **transfer** manages the communication between your application and external systems. It handles data fetching, API calls, and any data transformations needed for interaction with external services or databases. The transfer layer acts as a bridge between the domain and external resources, ensuring that all external interactions are isolated from the core business logic. ```bash mkdir src/features/organization/domain touch src/features/organization/domain/index.ts mkdir src/features/organization/presentation touch src/features/organization/presentation/index.ts mkdir src/features/organization/transfer touch src/features/organization/transfer/index.ts ``` > Note that feature name is singular because it represents a single cohesive unit of functionality. It emphasizes the idea of a focused, self-contained module. Each layer includes a root `index.ts` file for barrel exports. Only the exports from these index files should be used outside the layer. Adhering to this convention helps maintain a clear separation of concerns. ## Smart Root Components, Presentational Nested Components The key principle here is that the pages themselves should delegate as much logic as possible to underlying components as early as possible. To achieve this separation of concerns, I suggest creating a dedicated React component for each page. To emphasize that this code is independent of Next.js-specific conventions, I recommend storing these page components in a directory named after the corresponding feature. ```bash mkdir src/features/organization/presentation/OrganizationListPage touch src/features/organization/presentation/OrganizationListPage/OrganizationListPage.tsx touch src/features/organization/presentation/OrganizationListPage/index.ts echo "export * from './OrganizationListPage';" >> src/features/organization/presentation/OrganizationListPage/index.ts echo "export * from './OrganizationListPage';" >> src/features/organization/presentation/index.ts ``` Here’s the content for `OrganizationListPage.tsx`: ```typescript import { OrganizationListItem } from './OrganizationListItem'; type OrganizationProps = { organizations: { name: string; slug: string; isOwner: boolean; }[]; }; export const OrganizationListPage =({ organizations }: OrganizationProps): ReactNode => ( <> <h1>Organizations</h1> <ul> {organizations.map((organization) => ( <li key={organization.slug}> <OrganizationListItem {...organization} /> </li> ))} </ul> </> ); ``` The page component's sole responsibility is to define the internal layout of the page. All content rendering logic should be delegated to specialized, underlying components like `OrganizationListItem`, ensuring that the page remains lightweight and focused on assembling parts with clear and distinct responsibilities. Create an `OrganizationListItem` component in the `OrganizationListPage` folder. Components specific to a page are colocated with the page to make navigation between the root page and nested components simpler. To create the component, run: ```bash touch src/features/organization/presentation/OrganizationListPage/OrganizationListItem.tsx ``` Here is the content of the component: ```typescript type OrganizationListItemProps = { name: string; isOwner: boolean; }; export const OrganizationListItem = ({ name, isOwner }: OrganizationListItemProps): ReactNode => ( <> <h2>{name}</h2> {isOwner && <p>You are the owner of this organization</p>} </> ); ``` ## Minimize Direct Dependencies on Next.js At its core, **Next.js is primarily a way to organize routes**, combined with an intelligent builder that knows how to effectively split the app to provide the best possible rendering. In other frameworks, route organization typically occurs through one or more files that define the hierarchy of routes and their metadata, often structured in a format like JSON. Next.js offers a different approach by allowing the organization of routes through a folder hierarchy. **This aligns with the fundamental principle behind URLs: accessing a resource via a path**. However, contrary to what is often seen, I believe it’s important to **avoid assigning more responsibilities to Next.js than just routing and metadata management**, using it solely for what it excels at. To achieve this, I suggest using the `app` directory for everything related to Next.js: the folder hierarchy that maps out the routes and the files that represent the pages. ```bash mkdir src/app/organizations ``` > Note that the next folder route name is plural because it represents a collection of resources within the application. ```bash touch src/app/organizations/page.tsx ``` Here’s the content for `page.tsx`: ```typescript import { Metadata } from 'next'; import { OrganizationListPage } from '@/features/organization/presentation'; export const metadata: Metadata = { title: `Organizations | Next Stack` }; const organizations = [ { name: 'Tech for All', slug: 'tech-for-all', isOwner: true }, { name: 'Data Science Institute', slug: 'data-science-institute', isOwner: false }, { name: 'SmartCity Labs', slug: 'smartcity-labs', isOwner: false } ]; const Page = async (): Promise<ReactNode> => <OrganizationListPage organizations={organizations} />; export default Page; ``` As you can see, the page performs only a few key tasks: - Sets the metadata title. - Retrieves the data to display (using a local collection in this example). - Invokes the nested `OrganizationListPage` component, which is solely responsible for rendering. ## Maximize Server-Side Rendering for Components To enhance performance and SEO, it's beneficial to maximize server-side rendering (SSR) for components wherever possible. This is especially true for components that rely on data fetching or dynamic content. By keeping **data fetching logic** calls in app pages, you ensure that your nested components only focus on rendering. In our example, the organizations list is fetched directly in `organizations` root page, allowing `OrganizationListPage` to focus purely on presentation. Create `organizations.query` file in folder `src/features/organization/transfer/organizations.query.tsx`: ```bash mkdir src/features/organization/transfer/queries touch src/features/organization/transfer/queries/organizations.query.ts ``` Here’s the content for `organizations.query`: ```ts type Organization = { name: string; slug: string; isOwner: boolean; }; export const organizationsQuery = async (): Promise<Organization[]> => { return Promise.resolve([ { name: 'Tech for All', slug: 'tech-for-all', isOwner: true }, { name: 'Data Science Institute', slug: 'data-science-institute', isOwner: false }, { name: 'SmartCity Labs', slug: 'smartcity-labs', isOwner: false } ]); }; ``` Export `organizations.query` from transfer index file: ```bash touch src/features/organization/transfer/queries/index.ts echo "export * from './organizations.query';" >> src/features/organization/transfer/queries/index.ts touch src/features/organization/transfer/index.ts echo "export * from './queries';" >> src/features/organization/transfer/index.ts ``` Now you can update organizations page to include organizations query from transfer layer: ```typescript import { Metadata } from 'next'; import { OrganizationListPage } from '@/features/organization/presentation'; import { organizationsQuery } from '@/features/organization/transfer'; export const metadata: Metadata = { title: `Organizations | Next Stack` }; const Page = async (): Promise<ReactNode> => <OrganizationListPage organizations={await organizationsQuery()} />; export default Page; ``` In this example, there’s no need to use `'use client'`. In general, it's better to **avoid using it** unless absolutely necessary, as client-side rendering introduces extra overhead, reducing both performance and SEO benefits. However, there are cases where client-side logic is legitimately required, such as handling interactive elements or stateful components. In such cases, you should limit its scope to the smallest, most specific component that requires it. By narrowing the use of `'use client'` to **leaf components** rather than applying it to higher-level components, you: - **Reduce the client-side bundle size**, keeping the rest of your app optimized for server-side rendering. - **Maintain a clear separation of concerns**, ensuring that client-specific logic doesn't leak into components that could otherwise remain server-rendered. This approach helps you strike a balance between **client interactivity** and **server-side efficiency**, making your application both **performant** and **maintainable**. ## File Naming Conventions To maintain **clarity** and **consistency** across your codebase, it's essential to follow a **structured naming convention**. This improves navigation, readability, and allows you to quickly understand the role of each file. Use **PascalCase** for all React components, which should reside in the `presentation` layer. This aligns with common conventions in the React ecosystem, making it easy to identify React components. Files representing **business logic** within the `domain` layer should be named in lowercase, reflecting the business concepts they correspond to, **without any unnecessary prefixes or suffixes**. Adding words like `model`, `entity`... introduces technical jargon that dilutes the focus on the core business meaning. The **domain layer should remain pure**, focused solely on the business logic, and be **easily understandable by non-developers** familiar with the business. This ensures the code mirrors the domain's terminology, keeping it clear, concise, and relevant to stakeholders. For all other files outside of the `domain` layer and React components, use the naming convention: `[name].[kind].ts`. This format follows lowercase conventions and consists of two parts: - `name`: **Represents the specific concept** the file addresses. It should be descriptive enough to immediately convey what part of the application it concerns. - `kind`: **Denotes the purpose** of the file in the project. This helps distinguish files between a set of different responsibilities, such as `query`, `action`, `transfer`. You can create as many kind labels as needed to classify various common concepts. This naming approach ensures consistency, clarity, and easier navigation, making it simpler to identify the purpose of each file and maintain a well-organized codebase. ## Home page By default, Next.js places the home `page.tsx` at the root of the `app` directory. However, as the project grows, keeping all pages directly under `app/` can become messy. To maintain a **clean and structured hierarchy**, we move the home page into a dedicated `(main)` folder: ```bash mkdir src/app/\(main\) mv src/app/page.tsx src/app/\(main\)/page.tsx ``` <!-- todo --> ## Use Dependency Injection to maintain Pure Domain Logic ## Avoid Writing Scoped Styles --- [Next](https://hackmd.io/5fXHWvh4Swu7Kbp5SRaAkg) [Home](https://hackmd.io/9GoEP2IbT6SIJQHOf03PbA)