Features Deep Dive
Understanding Feature Architecture & Complexity
This page explains the Backend / Tech-Stack / Frontend structure and all the variations you'll encounter when building features.
Remember: This is our React marketplace's pattern. Your marketplace can organize features differently (Level 1 Personalization).
Why Backend / Tech-Stack / Frontend?
The Problem We're Solving
Features need to:
- Separate concerns - Business logic shouldn't be tied to UI
- Share contracts - Frontend and backend need consistent types
- Support multiple UIs - Same logic, different UI libraries
- Be testable - Logic should be testable without UI
- Handle different SDKs - Some features use SDKs, some use APIs, some are custom
Our Solution: 3 Layers
features/[name]/
βββ backend/[framework]/ (Optional - custom business logic)
βββ tech-stack/ (Required - the contract)
βββ frontend/[ui-library]/ (Required - the UI)Each layer has a specific purpose:
Layer 1: Backend (Optional)
When Is Backend Needed?
Backend is OPTIONAL - only include if you have custom business logic not provided by SDKs/connectors.
β You NEED Backend When:
1. Custom Business Logic
// Example: Teams Management
// Backend needed for custom team operations
features/teams-management/backend/nextjs/
βββ api-teams-route.ts // Custom team CRUD
βββ api-invites-route.ts // Custom invite workflow
βββ api-analytics-route.ts // Custom analyticsWhy? Teams have complex business rules (permissions, hierarchies, quotas) not provided by any SDK.
2. Complex Workflows
// Example: Multi-step onboarding
features/onboarding/backend/nextjs/
βββ api-steps-route.ts // Track onboarding progress
βββ api-validation-route.ts // Validate each step
βββ api-completion-route.ts // Complete onboardingWhy? Orchestrating multiple steps with state transitions requires custom API routes.
3. Data Transformation
// Example: Analytics aggregation
features/analytics/backend/nextjs/
βββ api-aggregate-route.ts // Aggregate raw dataWhy? Complex data processing should happen on backend, not frontend.
β You DON'T NEED Backend When:
1. SDK Handles Everything
// Example: Auth with Better Auth
// NO backend folder needed!
features/auth/
βββ tech-stack/ // Schemas, types, fallback hooks
βββ frontend/shadcn/ // UI components
// Why? Better Auth connector provides:
// - API routes (via connector)
// - Native hooks (via connector)
// - All auth logic (via adapter + connector)The connector does the work:
connectors/auth/better-auth-nextjs/
βββ api/auth/[...all]/route.ts β API routes here!
βββ middleware.ts β Middleware here!
βββ hooks.ts β Native hooks here!2. External API (No Server Logic Needed)
// Example: External payment processor
// NO backend folder needed!
features/payments/
βββ tech-stack/ // Schemas, types
βββ frontend/shadcn/ // Checkout UI
// Why? Stripe connector handles server:
connectors/payment/stripe-nextjs-drizzle/
βββ api/webhooks/stripe/route.ts β Webhooks here!
βββ lib/stripe/server.ts β Server client here!The Pattern Determines Backend Presence
SDK-Driven Pattern:
features/auth/
βββ tech-stack/ β Required
βββ frontend/shadcn/ β Required
NO backend/ folder! β SDK-Backend Connector IS the backend
{
"role": "tech-stack-layer",
"pattern": "tech-agnostic" // β Tech-stack in SDK-Driven
}Custom-Logic Pattern:
features/ai-chat/
βββ backend/nextjs/ β Required (custom logic)
βββ tech-stack/ β Required (wraps backend)
βββ frontend/shadcn/ β Required
{
"role": "backend-feature",
"pattern": "custom-logic" // β Backend in Custom-Logic
}Rule: If an SDK-Backend Connector exists for your feature (e.g., better-auth-nextjs), use SDK-Driven (no backend folder). Otherwise, use Custom-Logic (backend folder required).
Layer 2: Tech-Stack (Required - The Contract)
Always Present - The Single Source of Truth
The tech-stack is THE CONTRACT between backend and frontend.
What Goes in Tech-Stack?
1. Zod Schemas (Validation)
// features/auth/tech-stack/schemas.ts
import { z } from 'zod';
export const loginSchema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Password must be 8+ characters'),
});
export const signupSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
name: z.string().min(2),
});Used by:
- Backend: Server-side validation
- Frontend: Client-side validation
- Forms: React Hook Form integration
2. TypeScript Types
// features/auth/tech-stack/types.ts
export type LoginInput = z.infer<typeof loginSchema>;
export type SignupInput = z.infer<typeof signupSchema>;
export interface User {
id: string;
email: string;
name: string;
role: 'user' | 'admin';
}
export interface Session {
user: User;
expiresAt: Date;
}Used by:
- Backend: Type-safe API handlers
- Frontend: Type-safe components
- Hooks: Type-safe data fetching
3. Generic Hooks (Fallbacks - Priority 1)
// features/auth/tech-stack/hooks.ts
import { useQuery } from '@tanstack/react-query';
export function useSession() {
return useQuery({
queryKey: ['session'],
queryFn: async () => {
const res = await fetch('/api/auth/session');
if (!res.ok) throw new Error('Failed to fetch session');
return res.json();
},
});
}These are FALLBACKS:
- Provide generic fetch-based implementation
- Work with any backend
- Can be overridden by connectors (priority 2)
4. UI-Only Stores (Zustand)
// features/auth/tech-stack/stores.ts
import { create } from 'zustand';
export const useAuthUIStore = create((set) => ({
showLoginModal: false,
showSignupModal: false,
openLoginModal: () => set({ showLoginModal: true }),
closeLoginModal: () => set({ showLoginModal: false }),
openSignupModal: () => set({ showSignupModal: true }),
closeSignupModal: () => set({ showSignupModal: false }),
}));Important: Stores are for UI-only state:
- β Modal visibility
- β Filters, sorting
- β Form drafts
- β Local preferences
- β NOT for server data (that's TanStack Query's job)
Layer 3: Frontend (Required - The UI)
UI Components Consuming the Contract
Frontend imports from tech-stack and renders UI.
// features/auth/frontend/shadcn/LoginForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { loginSchema } from '@/lib/auth/schemas'; // From tech-stack
import { useSession } from '@/lib/auth'; // From tech-stack or connector
import { useAuthUIStore } from '@/lib/auth/stores'; // From tech-stack
import { Button } from '@/components/ui/button'; // shadcn/ui
export function LoginForm() {
const { closeLoginModal } = useAuthUIStore();
const { data: session, refetch } = useSession();
const form = useForm({
resolver: zodResolver(loginSchema),
defaultValues: { email: '', password: '' },
});
// ... UI code
}Frontend is swappable:
features/auth/frontend/shadcn/- shadcn/ui implementationfeatures/auth/frontend/mui/- Material-UI implementationfeatures/auth/frontend/chakra/- Chakra UI implementation
All import from the same tech-stack!
Feature Patterns by Integration Type
Pattern 1: SDK-Based Features (No Backend Needed)
Characteristics:
- SDK provides complete functionality
- Connector handles framework integration
- Backend folder not needed
Examples:
Auth (Better Auth)
Adapter: adapters/auth/better-auth/
β Provides: Universal auth config
Connector: connectors/auth/better-auth-nextjs/
β Provides: API routes, middleware, native hooks
Feature: features/auth/
βββ tech-stack/ (Schemas, types, UI stores)
βββ frontend/ (UI components)
β NO backend/ (Connector handles it!)Why no backend? Better Auth SDK + connector provide everything.
AI Chat (Vercel AI SDK)
Adapter: adapters/ai/vercel-ai-sdk/
β Provides: AI SDK configuration
Connector: connectors/ai/vercel-ai-nextjs/
β Provides: Streaming API routes, native hooks
Feature: features/ai-chat/
βββ tech-stack/ (Chat types, message schemas)
βββ frontend/ (Chat UI)
β NO backend/ (Connector handles it!)Why no backend? Vercel AI SDK + connector handle streaming.
Pattern 2: External API Features (Minimal Backend)
Characteristics:
- External service provides functionality
- Connector handles webhooks/server integration
- Backend only if custom logic needed
Example:
Payments (Stripe)
Adapter: adapters/payment/stripe/
β Provides: Stripe client config
Connector: connectors/payment/stripe-nextjs-drizzle/
β Provides: Webhook handlers, checkout API, server utils
Feature: features/payments/
βββ backend/ (Optional - custom pricing logic)
βββ tech-stack/ (Schemas, types)
βββ frontend/ (Checkout UI)Backend optional because:
- Stripe API handles core functionality
- Connector handles webhooks
- Backend only needed for custom business rules (e.g., dynamic pricing)
Email (Resend)
Adapter: adapters/email/resend/
β Provides: Resend client
Connector: connectors/email/resend-nextjs/
β Provides: Email API, templates
Feature: features/emailing/
βββ backend/ (Optional - campaign logic)
βββ tech-stack/ (Email schemas, types)
βββ frontend/ (Email composer)Backend optional because:
- Resend API sends emails
- Connector provides email API
- Backend only for complex campaigns
Pattern 3: Custom Features (Backend Required)
Characteristics:
- No SDK provides the functionality
- Custom business logic required
- Full backend needed
Example:
Teams Management
Feature: features/teams-management/
βββ backend/nextjs/
β βββ api-teams-route.ts β Custom CRUD
β βββ api-invites-route.ts β Custom invites
β βββ api-analytics-route.ts β Custom analytics
βββ tech-stack/
β βββ schemas.ts β Team schemas
β βββ types.ts β Team types
β βββ hooks.ts β TanStack Query hooks
β βββ stores.ts β UI stores
βββ frontend/shadcn/
βββ TeamsList.tsx
βββ TeamSettings.tsx
βββ InviteMember.tsxBackend required because:
- β Complex team hierarchies
- β Custom permission logic
- β Invite workflow with expiration
- β Team analytics aggregation
- β No SDK provides this
The Backend Overwrites Hooks Pattern
How Connectors Provide Better Hooks
When a connector has SDK-native hooks, it can override the generic tech-stack hooks.
Priority System:
// Priority 1: Tech-Stack (Generic Fallback)
// features/auth/tech-stack/hooks.ts
export function useSession() {
// Generic fetch-based implementation
return useQuery({
queryKey: ['session'],
queryFn: async () => {
const res = await fetch('/api/auth/session');
return res.json();
},
});
}
// Deployed to: src/lib/auth/hooks.ts (priority: 1)// Priority 2: Connector (SDK-Native - OVERWRITES)
// connectors/auth/better-auth-nextjs/hooks.ts
export { useSession } from '@/lib/auth/client'; // Better Auth native
// Deployed to: src/lib/auth/hooks.ts (priority: 2) β WINS!Result:
// Frontend imports from unified path
import { useSession } from '@/lib/auth';
// Gets Better Auth native hook if connector present
// Gets generic fetch hook if no connectorWhy This Pattern?
1. Progressive Enhancement
- Start with generic hooks (work with any backend)
- Connector adds optimized SDK-native hooks
- Frontend code doesn't change
2. SDK Advantages
// Generic hook:
useQuery({ queryFn: () => fetch('/api/session') })
// Better Auth native hook:
useSession()
β
Built-in SSR support
β
Automatic revalidation
β
Optimized caching
β
Type-safe3. Tech-Stack Remains Agnostic
- Tech-stack doesn't know about Better Auth
- Connector adds Better Auth integration
- Can swap auth providers without touching tech-stack
Decision Tree: Do I Need a Backend?
START: I'm building a feature
ββ Does an SDK provide this functionality?
ββ YES β Does the SDK need custom business logic?
β ββ NO β β
NO BACKEND NEEDED
β β Example: Auth (Better Auth handles everything)
β β
β ββ YES β β οΈ BACKEND OPTIONAL (for custom logic only)
β Example: Payments (Stripe API + custom pricing)
β
ββ NO β Is this calling an external API?
ββ YES β β οΈ BACKEND OPTIONAL (for orchestration)
β Example: Email (Resend API + campaign logic)
β
ββ NO β β
BACKEND REQUIRED (custom functionality)
Example: Teams (no SDK, custom everything)Real-World Examples
Example 1: Auth Feature (No Backend)
Structure:
features/auth/
βββ tech-stack/
β βββ schemas.ts (Login/signup schemas)
β βββ types.ts (User, Session types)
β βββ hooks.ts (Generic fallbacks)
β βββ stores.ts (UI state: modals)
βββ frontend/shadcn/
βββ LoginForm.tsx
βββ SignupForm.tsx
βββ UserMenu.tsx
β NO backend/ folder!Why?
Adapter: adapters/auth/better-auth/
β Universal auth config
Connector: connectors/auth/better-auth-nextjs/
β API routes: /api/auth/[...all]
β Middleware: session checking
β Native hooks: useSession(), signIn(), signOut()
Feature: features/auth/
β Schemas & types (contract)
β UI components (presentation)The connector does the backend work!
Example 2: Payments Feature (Optional Backend)
Structure:
features/payments/
βββ backend/stripe-nextjs/ (Optional)
β βββ api-pricing-route.ts (Custom dynamic pricing logic)
βββ tech-stack/
β βββ schemas.ts (Checkout schemas)
β βββ types.ts (Product, Price types)
β βββ hooks.ts (useCheckout, useProducts)
β βββ stores.ts (Cart state)
βββ frontend/shadcn/
βββ CheckoutPage.tsx
βββ PricingTable.tsx
βββ Cart.tsxWhy optional backend?
Adapter: adapters/payment/stripe/
β Stripe client config
Connector: connectors/payment/stripe-nextjs-drizzle/
β Webhook handler: /api/webhooks/stripe
β Checkout API: /api/checkout
β Product sync
Feature Backend: features/payments/backend/
β ONLY if you need custom pricing logic
β Example: Volume discounts, A/B testing prices
β Otherwise, connector handles everythingExample 3: Teams Feature (Backend Required)
Structure:
features/teams-management/
βββ backend/nextjs/
β βββ api-teams-route.ts (CRUD operations)
β βββ api-invites-route.ts (Invite workflow)
β βββ api-members-route.ts (Member management)
β βββ api-analytics-route.ts (Team analytics)
βββ tech-stack/
β βββ schemas.ts (Team, Invite schemas)
β βββ types.ts (Team, Member types)
β βββ hooks.ts (useTeams, useInvites)
β βββ stores.ts (UI state: filters)
βββ frontend/shadcn/
βββ TeamsList.tsx
βββ TeamSettings.tsx
βββ InviteMembers.tsx
βββ TeamAnalytics.tsxWhy backend required?
β No SDK provides team management
β No external API handles hierarchies
β
Custom business logic needed:
- Team hierarchies (parent/child)
- Permission systems (owner, admin, member)
- Invite workflows (expiration, acceptance)
- Quota management (team size limits)
- Analytics aggregationBest Practices
1. Start Simple, Add Complexity When Needed
β Good:
// Start with SDK-based feature (no backend)
features/auth/
βββ tech-stack/
βββ frontend/
// Add backend ONLY when you need custom logic
features/auth/
βββ backend/ β Added later for custom workflow
βββ tech-stack/
βββ frontend/β Bad:
// Don't create backend "just in case"
features/auth/
βββ backend/ β Empty folder, no files!
βββ tech-stack/
βββ frontend/2. Tech-Stack Is Always Required
β Good:
// Always have tech-stack (the contract)
features/[name]/
βββ tech-stack/ β Always present
β βββ schemas.ts β At minimum
β βββ types.ts β At minimum
βββ frontend/β Bad:
// Don't skip tech-stack
features/[name]/
βββ frontend/ β Types inline? β3. Separate Server State from UI State
β Good:
// Server state: TanStack Query
export function useTeams() {
return useQuery({ /* ... */ });
}
// UI state: Zustand
export const useTeamsUIStore = create({
showCreateModal: false,
filterBy: 'all',
});β Bad:
// Don't mix server data in Zustand store
export const useTeamsStore = create({
teams: [], // β Server data
showModal: false, // β
UI state
});4. Use Backend Overwrites Hooks When Available
β Good:
// Connector provides SDK-native hooks (priority 2)
// connectors/auth/better-auth-nextjs/hooks.ts
export { useSession } from '@/lib/auth/client';
// Frontend gets optimized hook automatically
import { useSession } from '@/lib/auth'; // β Better Auth nativeβ Bad:
// Don't bypass the connector
// Frontend importing SDK directly
import { useSession } from 'better-auth/client'; // β Tight couplingSummary
Feature Backend Decision Matrix
| Integration Type | Backend Needed? | Example | Why? |
|---|---|---|---|
| SDK-Based | β No | Auth (Better Auth) | SDK + connector handle everything |
| External API | β οΈ Optional | Payments (Stripe) | API handles core, backend for custom logic |
| Custom | β Yes | Teams Management | No SDK, custom business logic required |
Layer Purposes
| Layer | Purpose | Always Present? |
|---|---|---|
| Backend | Custom business logic | β οΈ Optional |
| Tech-Stack | Contract (schemas, types, hooks) | β Required |
| Frontend | UI presentation | β Required |
Key Patterns
- Backend Overwrites Hooks - Connectors provide SDK-native hooks (priority 2) that override tech-stack fallbacks (priority 1)
- UI-Only Stores - Zustand for UI state, TanStack Query for server data
- Progressive Enhancement - Start simple (SDK), add backend when needed
Next Steps
- Adapters Deep Dive β - Universal SDK configuration
- Connectors Deep Dive β - Framework integration
- Architecture Overview β - Complete system architecture
- Contributing β - Build your own features
Remember: This is our React marketplace's pattern. Your marketplace can organize features however makes sense for your ecosystem!