Skip to main content

Feature Structure

We use a Domain-Driven Design (DDD) approach for folder organization. Everything related to a specific business domain (e.g., Cameras) stays together in the features/ directory.

Anatomy of a Feature

Taking features/cameras as an example:

features/cameras/
├── api.ts # RAW API calls (fetch/axios wrappers)
├── types.ts # TypeScript interfaces & Enums
├── hooks.ts # React Query hooks (useCameras, useCamera)
├── components/ # UI Components specific to this feature
│ ├── camera-card.tsx
│ ├── camera-form.tsx
│ └── metrics-chart.tsx
└── index.ts # Public API (What is exposed to the rest of the app)

1. api.ts

Contains pure async functions that return Promises. They use the global apiClient.

export const fetchCameras = async (params) => {
return apiClient.get("/cameras", { params });
};

2. hooks.ts

Connects the api.ts functions to TanStack Query. This is where caching key strategies are defined.

export const useCameras = (filters) => {
return useQuery({
queryKey: ["cameras", filters],
queryFn: () => fetchCameras(filters),
});
};

3. components/

Contains the Views.

  • Heavy Logic: camera-form.tsx (Zod validation, API mutation).
  • Presentation: camera-card.tsx (Displays a single camera).
  • Analytics: agitation-trend-chart.tsx, metrics-chart.tsx.

Why this structure?

  • Isolation: You can delete the features/cameras folder and only that feature disappears.
  • Discoverability: "Where is the code for the Camera Card?" -> features/cameras/components.
  • Scalability: Adding a new feature doesn't clutter the global components folder.