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:

  1. Separate concerns - Business logic shouldn't be tied to UI
  2. Share contracts - Frontend and backend need consistent types
  3. Support multiple UIs - Same logic, different UI libraries
  4. Be testable - Logic should be testable without UI
  5. 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 analytics

Why? 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 onboarding

Why? 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 data

Why? 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 implementation
  • features/auth/frontend/mui/ - Material-UI implementation
  • features/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.tsx

Backend 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 connector

Why 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-safe

3. 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.tsx

Why 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 everything

Example 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.tsx

Why 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 aggregation

Best 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 coupling

Summary

Feature Backend Decision Matrix

Integration TypeBackend Needed?ExampleWhy?
SDK-Based❌ NoAuth (Better Auth)SDK + connector handle everything
External API⚠️ OptionalPayments (Stripe)API handles core, backend for custom logic
Customβœ… YesTeams ManagementNo SDK, custom business logic required

Layer Purposes

LayerPurposeAlways Present?
BackendCustom business logic⚠️ Optional
Tech-StackContract (schemas, types, hooks)βœ… Required
FrontendUI presentationβœ… Required

Key Patterns

  1. Backend Overwrites Hooks - Connectors provide SDK-native hooks (priority 2) that override tech-stack fallbacks (priority 1)
  2. UI-Only Stores - Zustand for UI state, TanStack Query for server data
  3. Progressive Enhancement - Start simple (SDK), add backend when needed

Next Steps


Remember: This is our React marketplace's pattern. Your marketplace can organize features however makes sense for your ecosystem!