guardian-framework 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,383 @@
1
+ ---
2
+ name: nextjs-enterprise-codegen
3
+ description: Full reference for Next.js enterprise code generation with DDD + Clean Architecture. Covers module structure, server/client boundary, data fetching, state management, design system integration, error handling, testing, security, performance, and accessibility. Loaded on-demand via agents/nextjs-codegen.md skill.
4
+ ---
5
+
6
+ # Next.js Enterprise Code Generation — DDD + Clean Architecture
7
+
8
+ > Canonical skill for generating production-grade Next.js code with DDD patterns.
9
+ > All code MUST follow these patterns. Validators enforce compliance.
10
+ >
11
+ > For design system and styling patterns, see: `.pi/skills/design-system-enterprise-codegen.md`
12
+
13
+ ---
14
+
15
+ ## 1. Project Structure — DDD with Next.js App Router
16
+
17
+ ```
18
+ src/
19
+ module/ # One bounded context
20
+ domain/ # Pure business logic, zero framework imports
21
+ entity.ts # Domain entities with encapsulated state
22
+ value.ts # Immutable value objects
23
+ events.ts # Domain events
24
+ errors.ts # Typed error classes
25
+ application/ # Application logic
26
+ store.ts # Zustand / Jotai state (client-only)
27
+ actions.ts # Server Actions (server-only)
28
+ queries.ts # React Query hooks for data fetching
29
+ dto.ts # Command/Query DTOs
30
+ infrastructure/ # External adapters
31
+ api-client.ts # fetch wrapper, error handling, retry
32
+ auth.ts # Auth provider SDK wrapper
33
+ analytics.ts # Analytics SDK wrapper
34
+ interfaces/ # UI layer
35
+ page.tsx # App Router page (default: Server Component)
36
+ component.client.tsx # Client Component with 'use client'
37
+ layout.tsx # Layout wrapper
38
+ shared/ # Cross-module shared code
39
+ ui/ # Design system components
40
+ button/
41
+ card/
42
+ modal/
43
+ lib/ # Shared utilities
44
+ hooks/ # Shared React hooks
45
+ ```
46
+
47
+ ### Dependency Rule
48
+ ```
49
+ domain → application → infrastructure → interfaces
50
+
51
+ shared/ (no framework deps)
52
+ ```
53
+
54
+ - **domain/** — zero React/Next.js imports. Pure TypeScript.
55
+ - **application/** — imports from domain. May import from `shared/lib/`.
56
+ - **infrastructure/** — imports from domain + application. Wraps external SDKs.
57
+ - **interfaces/** — imports from application. Contains all React/Next.js code.
58
+
59
+ ---
60
+
61
+ ## 2. Server/Client Boundary
62
+
63
+ This is the most critical architectural decision in Next.js. Get it wrong and you lose the benefits of both paradigms.
64
+
65
+ ### What goes where
66
+
67
+ | Layer | Renders on | 'use client'? | Can use |
68
+ |-------|-----------|---------------|---------|
69
+ | `interfaces/page.tsx` | Server | No | `async`, `cookies()`, `headers()`, direct DB |
70
+ | `interfaces/*.client.tsx` | Client | Yes | `useState`, `useEffect`, `onClick`, browser APIs |
71
+ | `application/store.ts` | Client | Depends | Zustand, Jotai |
72
+ | `application/actions.ts` | Server | No | `'use server'`, `revalidatePath()` |
73
+ | `application/queries.ts` | Client | Yes | React Query, SWR |
74
+ | `domain/`, `infrastructure/` | Both | No | Pure TS, fetch |
75
+
76
+ ### Pattern: Island Architecture
77
+
78
+ ```
79
+ ┌─────────────────────────────────────────┐
80
+ │ page.tsx (Server Component) │
81
+ │ <header>Static header</header> │
82
+ │ <ProductList> │
83
+ │ {products.map(p => <ProductCard/>)} │ ← Server-rendered
84
+ │ </ProductList> │
85
+ │ <CheckoutButton.client.tsx> │ ← Client island
86
+ │ <AddToCartButton/> │
87
+ │ <PriceSummary/> │
88
+ │ </CheckoutButton.client.tsx> │
89
+ └─────────────────────────────────────────┘
90
+ ```
91
+
92
+ ### Rules
93
+ - ✅ Default to Server Components. Add `'use client'` only when you need interactivity.
94
+ - ✅ Push client state DOWN to the leaf components that need it.
95
+ - ✅ Use Server Actions for mutations, client handlers for optimistic updates.
96
+ - ❌ Never put `'use client'` on a page wrapper — only on interactive islands.
97
+ - ❌ Never import server-only code (actions, DB queries) in client components.
98
+
99
+ ---
100
+
101
+ ## 3. Data Fetching Patterns
102
+
103
+ ### Server Component Fetch (Default)
104
+
105
+ ```typescript
106
+ // interfaces/products/page.tsx — Server Component, async
107
+ import { ProductService } from '../../application/actions';
108
+
109
+ export default async function ProductsPage() {
110
+ const products = await ProductService.list(); // Direct call, no API route
111
+
112
+ return (
113
+ <div>
114
+ {products.map(p => (
115
+ <ProductCard key={p.id} product={p} /> // Server-rendered
116
+ ))}
117
+ </div>
118
+ );
119
+ }
120
+ ```
121
+
122
+ ### Client Data Fetching (React Query)
123
+
124
+ ```typescript
125
+ // application/queries.ts
126
+ import { useQuery } from '@tanstack/react-query';
127
+ import { apiClient } from '../infrastructure/api-client';
128
+
129
+ export function useProducts() {
130
+ return useQuery({
131
+ queryKey: ['products'],
132
+ queryFn: () => apiClient.get('/api/products'),
133
+ staleTime: 30_000, // 30s before refetch
134
+ gcTime: 5 * 60 * 1000, // 5min cache
135
+ });
136
+ }
137
+
138
+ // interfaces/product-list.client.tsx
139
+ 'use client';
140
+ export function ProductList() {
141
+ const { data, isLoading } = useProducts();
142
+ if (isLoading) return <Skeleton />;
143
+ return data.map(p => <ProductCard key={p.id} product={p} />);
144
+ }
145
+ ```
146
+
147
+ ### Server Actions (Mutations)
148
+
149
+ ```typescript
150
+ // application/actions.ts
151
+ 'use server';
152
+ import { revalidatePath } from 'next/cache';
153
+ import { Cart } from '../domain/entity';
154
+
155
+ export async function addToCart(productId: string, quantity: number) {
156
+ const cart = await loadCart();
157
+ cart.addItem(productId, quantity);
158
+ await saveCart(cart);
159
+ revalidatePath('/cart'); // Revalidate without full page reload
160
+ return { success: true };
161
+ }
162
+ ```
163
+
164
+ ### Rules
165
+ - ✅ Server Components for initial data — zero client JS for the first render
166
+ - ✅ React Query for client-side data that needs realtime/stale-while-revalidate
167
+ - ✅ Server Actions for mutations — no API route boilerplate
168
+ - ❌ Never fetch in Server Components for data that needs realtime updates
169
+ - ❌ Never use `useEffect` for data fetching — use React Query or Server Actions
170
+
171
+ ---
172
+
173
+ ## 4. State Management
174
+
175
+ | State type | Tool | Location |
176
+ |-----------|------|----------|
177
+ | Server state (data) | React Query / SWR | `application/queries.ts` |
178
+ | Client state (UI) | Zustand / Jotai | `application/store.ts` |
179
+ | URL state (filters, page) | `useSearchParams` | `interfaces/*.client.tsx` |
180
+ | Form state | React Hook Form + Zod | `interfaces/*.client.tsx` |
181
+ | Server mutations | Server Actions | `application/actions.ts` |
182
+
183
+ ```typescript
184
+ // application/store.ts — Zustand store
185
+ import { create } from 'zustand';
186
+ import { devtools } from 'zustand/middleware';
187
+ import { Cart } from '../domain/entity';
188
+
189
+ interface CartState {
190
+ cart: Cart | null;
191
+ isOpen: boolean; // UI state — cart drawer toggle
192
+ addItem: (productId: string, qty: number) => Promise<void>;
193
+ toggleCart: () => void;
194
+ }
195
+
196
+ export const useCartStore = create<CartState>()(
197
+ devtools(
198
+ (set) => ({
199
+ cart: null,
200
+ isOpen: false,
201
+ addItem: async (productId, qty) => {
202
+ // Call Server Action, update local state optimistically
203
+ },
204
+ toggleCart: () => set(s => ({ isOpen: !s.isOpen })),
205
+ }),
206
+ { name: 'cart-store' },
207
+ ),
208
+ );
209
+ ```
210
+
211
+ ### Rules
212
+ - ✅ Separate server state (React Query) from client state (Zustand)
213
+ - ✅ Keep UI-only state (modals, toggles) local with `useState` when possible
214
+ - ✅ Use Zustand slices for complex domains
215
+ - ❌ Don't put server data in Zustand — that's what React Query is for
216
+
217
+ ---
218
+
219
+ ## 5. Design System & CSS Architecture
220
+
221
+ ### Structure
222
+
223
+ ```
224
+ shared/
225
+ ui/ # Design system components
226
+ tokens/ # Design tokens
227
+ colors.ts
228
+ typography.ts
229
+ spacing.ts
230
+ button/
231
+ button.tsx # Component
232
+ button.module.css # Styles
233
+ button.test.tsx # Tests
234
+ card/
235
+ modal/
236
+ form/
237
+ input.tsx
238
+ select.tsx
239
+ ```
240
+
241
+ ### Styling Strategy
242
+
243
+ | Approach | When to use |
244
+ |----------|-------------|
245
+ | CSS Modules (`*.module.css`) | Component-scoped styles in design system |
246
+ | Tailwind utility classes | Layout, spacing, one-off adjustments |
247
+ | CSS Variables (`var(--color-primary)`) | Design tokens, theming |
248
+ | `clsx` / `tailwind-merge` | Conditional class composition |
249
+
250
+ ```typescript
251
+ // shared/ui/button/button.tsx
252
+ import { type VariantProps, cva } from 'class-variance-authority';
253
+ import { cn } from '../../lib/utils';
254
+
255
+ const buttonVariants = cva(
256
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors',
257
+ {
258
+ variants: {
259
+ variant: {
260
+ primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
261
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
262
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
263
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
264
+ },
265
+ size: {
266
+ sm: 'h-9 px-3',
267
+ md: 'h-10 px-4 py-2',
268
+ lg: 'h-11 px-8',
269
+ },
270
+ },
271
+ defaultVariants: { variant: 'primary', size: 'md' },
272
+ },
273
+ );
274
+
275
+ interface ButtonProps
276
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
277
+ VariantProps<typeof buttonVariants> {}
278
+
279
+ export function Button({ className, variant, size, ...props }: ButtonProps) {
280
+ return <button className={cn(buttonVariants({ variant, size }), className)} {...props} />;
281
+ }
282
+ ```
283
+
284
+ ### Design System Rules
285
+ - ✅ Design tokens in `shared/ui/tokens/` — single source of truth for colors, spacing, typography
286
+ - ✅ Components use `cva` (class-variance-authority) for variant props
287
+ - ✅ CSS Modules for component styles, Tailwind for layout
288
+ - ✅ Dark mode via `class` strategy on `<html>` element
289
+ - ❌ No inline styles (`style={{}}`) except for dynamic values (animations, transforms)
290
+ - ❌ No CSS-in-JS libraries (styled-components, emotion) — they break Server Components
291
+
292
+ ---
293
+
294
+ ## 6. Error Handling
295
+
296
+ ```typescript
297
+ // domain/errors.ts — Typed domain errors
298
+ export class DomainError extends Error {
299
+ constructor(
300
+ public readonly code: string,
301
+ message: string,
302
+ public readonly status: number = 400,
303
+ ) {
304
+ super(message);
305
+ this.name = 'DomainError';
306
+ }
307
+
308
+ static notFound(id: string) {
309
+ return new DomainError('NOT_FOUND', `Resource ${id} not found`, 404);
310
+ }
311
+ }
312
+
313
+ // interfaces/error.tsx — Next.js error boundary
314
+ 'use client';
315
+ export default function Error({ error, reset }: { error: Error; reset: () => void }) {
316
+ return (
317
+ <div role="alert">
318
+ <h2>Something went wrong</h2>
319
+ <button onClick={() => reset()}>Try again</button>
320
+ </div>
321
+ );
322
+ }
323
+
324
+ // interfaces/not-found.tsx — Next.js 404 boundary
325
+ export default function NotFound() {
326
+ return <div>Page not found</div>;
327
+ }
328
+ ```
329
+
330
+ ---
331
+
332
+ ## 7. Testing Patterns
333
+
334
+ | Test type | Tool | What to test |
335
+ |-----------|------|-------------|
336
+ | Unit | Vitest | `domain/` entities, value objects |
337
+ | Component | Vitest + Testing Library | `interfaces/` components |
338
+ | Integration | Vitest + MSW | API client with mocked server |
339
+ | E2E | Playwright | Full user flows |
340
+
341
+ ```typescript
342
+ // domain/entity.test.ts — Pure unit test, no React
343
+ import { Cart } from './entity';
344
+
345
+ describe('Cart', () => {
346
+ it('adds item', () => {
347
+ const cart = new Cart();
348
+ cart.addItem({ id: '1', name: 'Test', price: 100 }, 2);
349
+ expect(cart.total).toBe(200);
350
+ });
351
+ });
352
+ ```
353
+
354
+ ---
355
+
356
+ ## 8. Anti-Patterns — NEVER DO
357
+
358
+ ```typescript
359
+ // ❌ Business logic in components
360
+ export default function Page() {
361
+ const [cart, setCart] = useState([]); // BAD — belongs in domain/
362
+ }
363
+
364
+ // ❌ Data fetching in client components without React Query
365
+ useEffect(() => { fetch('/api/data').then(setData); }, []); // BAD
366
+
367
+ // ❌ 'use client' on the entire page
368
+ 'use client'; // BAD — only add to interactive islands
369
+
370
+ // ❌ Inline styles
371
+ <div style={{ marginLeft: '16px' }}> // BAD — use Tailwind or CSS Modules
372
+
373
+ // ❌ Mixing server/client concerns
374
+ import { cookies } from 'next/headers'; // BAD in client component
375
+
376
+ // ❌ Global CSS for component styles
377
+ h1 { color: blue; } // BAD — use CSS Modules or Tailwind
378
+ ```
379
+
380
+ ---
381
+
382
+ *Version: 1.1.0*
383
+ *Last updated: 2026-07-03*