---
title: "Code Structure"
description: "Next.js 16 App Router layout, feature modules, hooks, and provider SSOT (v1.6.4)"
locale: "en"
---
# Code Structure

Ring Platform v1.6.4 organizes code by **App Router route groups**, **feature domains**, and a **provider-owned hook layer** that prevents duplicate network subscriptions.

> **Info**
> Use **Founder** / **Developer** tabs in the docs sidebar to filter this page. Shared sections below apply to both audiences; audience-specific blocks follow.

## Repository layout

```
ring-platform.org/
├── app/                    # Next.js 16 App Router + app/api route handlers
├── components/             # Shared UI, navigation, providers
├── features/               # Domain modules (auth, store, wallet, news, …)
├── hooks/                  # Root hooks — see hooks/HOOKS-README.md
├── lib/                    # database, payments, locale-config, tunnel
├── services/               # email AI CRM, blockchain oracle
├── locales/                # next-intl JSON bundles (en, uk, ru)
├── data/                   # schema.sql + migrations/
└── docs/                   # MDX documentation (docs/{locale}/**)
```

**Not in public OSS tree:** `k8s/`, `cli/`, `propagation/` (gitignored).

| Area | Path |
|------|------|
| PaymentConductor | `lib/payments/conductor/` |
| Locale SSOT | `lib/locale-config.ts`, `i18n/routing.ts` |
| Database abstraction | `lib/database/` (`db()`, `getSharedPgPool()`) |
| Auth.js v5 | `auth.ts`, `features/auth/components/session-provider.tsx` |
| Client app shell | `components/providers/app-client-shell.tsx` |

### For founders

## Why structure matters for your clone

A Ring clone is a **white-label fork** of this tree. Feature code stays inside `features/{domain}/` so Reggie propagation and your customizations do not collide with core platform plumbing.

  
- **[Best practices](/docs/development/best-practices.md)** — Database, auth, and SSOT guardrails for clone operators hiring integrators.

  
- **[Performance patterns](/docs/features/performance.md)** — What keeps pages fast without extra infrastructure.

  
- **[Backend modes](/docs/architecture/backend-modes-and-databases.md)** — PostgreSQL-primary vs Firebase-backed deployments.

**Typical clone scenarios:**

- **Commerce ring** — extend `features/store/`, keep `StoreProvider` in the app shell.
- **Community ring** — entities + opportunities under `features/`, tunnel providers unchanged.
- **News/media ring** — `features/news/` services; do not duplicate global notification providers.

### For developers

## App Router

```
app/
├── (public)/[locale]/         # Marketing, store, blog
├── (authenticated)/[locale]/    # Editor, entities, settings
├── (admin)/[locale]/            # Admin panels
├── (confidential)/[locale]/     # Tier-gated docs
├── api/                         # Route handlers (auth, store, payments, …)
├── _actions/                    # Server Actions
└── layout.tsx
```

Route groups isolate layouts and auth boundaries. Server Actions live in `app/_actions/` (not `@actions/`).

## Features module pattern

```
features/{domain}/
├── components/
├── hooks/          # optional — domain-scoped only
├── services/
├── lib/
└── index.ts        # public API
```

Cross-feature imports should go through each feature's `index.ts` export surface, not deep paths.

## Hooks and providers

Root hooks live in `hooks/`. **Provider-owned subscriptions** prevent duplicate tunnel connections and API polls. Full reference: `hooks/HOOKS-README.md` (Provider matrix + **P4 — duplicate-fetch consolidation**, 2026-07-07).

If a hook opens a tunnel channel or polls an API globally (unread count, credit balance, FCM token), mount it **once** in a provider. UI components consume context — they do not call the raw hook.

```mermaid
flowchart TB
  subgraph shell["app-client-shell.tsx"]
    Session["SessionProvider\nfeatures/auth/components/session-provider.tsx"]
    WebVitals["WebVitalsProvider\nbatched useReportWebVitals"]
    Credit["CreditBalanceProvider"]
    FCM["FCMProvider"]
    Tunnel["TunnelProvider"]
    Web3["Web3ScopeProvider"]
    Store["StoreProvider"]
  end

  subgraph wallet["WalletWrapper (wallet routes)"]
    Notif["NotificationProvider\nuseUnreadCount"]
    Hist["CreditHistoryProvider"]
  end

  Session --> WebVitals
  Session --> Credit
  Credit --> Tunnel
  Tunnel --> Notif
```

### Provider matrix (verified)

| Hook / concern | Owner | Context / accessor | Notes |
|----------------|-------|-------------------|-------|
| `use-auth.ts` | `SessionProvider` | `useAuth()` | SSOT: `features/auth/components/session-provider.tsx` — **not** `components/providers/session-provider.tsx` (deleted) |
| `use-fcm.ts` | `FCMProvider` | internal / `useFCM()` in provider only | Token upsert via `app/_actions/fcm.ts` |
| `use-tunnel.ts` | `TunnelProvider` | `useTunnel()` | Shared WSS/SSE connection |
| `use-unread-count.ts` | `NotificationProvider` | `useNotificationContext()` | Nav badges must use context |
| `use-credit-balance.ts` | `CreditBalanceProvider` | `useCreditBalanceContext()` | Module-scope bootstrap single-flight (5s TTL, keyed by `userId`) |
| `use-credit-history.ts` | `CreditHistoryProvider` | `useCreditHistoryContext()` | Wallet routes only |
| `use-vendor-status.ts` | *(no provider)* | `useVendorStatus()` | Single-flight + 30s TTL; SSOT for `GET /api/vendor/status` |
| Web Vitals | `WebVitalsProvider` | — | Buffers metrics → one debounced `POST /api/analytics/web-vitals` |

### SessionProvider tuning

```typescript
// features/auth/components/session-provider.tsx

```

One 15-minute background poll; no refetch on focus/reconnect — cuts redundant `GET /api/auth/session` across every `useSession()` consumer.

### P4 duplicate-fetch fixes (2026-07-07)

Documented in `hooks/HOOKS-README.md` § P4:

- **Vendor status** — `hooks/use-vendor-status.ts` replaces inline `fetch('/api/vendor/status')` in sidebar layouts.
- **Credit balance bootstrap** — module-scope cache in `hooks/use-credit-balance.ts` survives Strict Mode remounts.
- **Web Vitals** — `components/providers/web-vitals-provider.tsx` batches `useReportWebVitals` (1.5s debounce).
- **Store catalog** — `features/store/config.ts` → `getCachedProductCatalog()` (`'use cache'` + `cacheTag('store:products')`).
- **SessionProvider** — merged tuned settings; deleted duplicates `components/providers/session-provider.tsx` and `auth-provider.tsx`.

Enforce locally:

```bash
./scripts/validate-provider-ssot.sh
```

## Lib directory essentials

| Module | Path | Role |
|--------|------|------|
| Database | `lib/database/` | `db()`, `getSharedPgPool()`, adapters |
| Auth config | `auth.ts` | Auth.js v5 handlers + `auth()` |
| Payments | `lib/payments/conductor/` | PaymentConductor |
| Locale | `lib/locale-config.ts` | SSOT for `en` / `uk` / `ru` |

> **Warning**
> Use `db()` from `@/lib/database` for all domain persistence. `getSharedPgPool()` is the sanctioned escape hatch for **PostGIS raw SQL only** — never `new Pool(` outside `lib/database/`.

## Server Actions

All Server Actions use file-level `'use server'` in `app/_actions/`. Derive `userId` from `await auth()` — never from client parameters. See [Best practices](/docs/development/best-practices.md) for the canonical pattern.

## Related documentation

  
- **[Best practices](/docs/development/best-practices.md)** — `db()` rules, SSOT script gates, single-flight client caches.

  
- **[Hooks README](/docs/development/code-structure.md#hooks-and-providers)** — In-repo SSOT: `hooks/HOOKS-README.md`.

  
- **[Realtime architecture](/docs/architecture/real-time.md)** — TunnelHub, WSS routes, notification channel naming.

  
- **[Push notifications](/docs/features/push-notifications-fcm.md)** — `useFCM`, Server Action registration, sign-out cleanup.
