aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
932 lines (716 loc) • 22.9 kB
Markdown
# Next.js + React Tech Preset
> Preset de arquitetura otimizado para desenvolvimento fullstack com Next.js e React, focado em máxima eficiência com Claude Code.
## Metadata
```yaml
preset:
id: nextjs-react
name: 'Next.js + React Fullstack Preset'
version: 1.0.0
description: 'Arquitetura otimizada para aplicações fullstack com Next.js 16+, React, TypeScript e padrões que maximizam a eficiência do Claude Code'
technologies:
- Next.js 16+ (App Router + Proxy)
- React 18+
- TypeScript
- Tailwind CSS
- Zustand
- React Query
- Zod
- Vitest
- Playwright
suitable_for:
- 'Aplicações web fullstack'
- 'SaaS products'
- 'E-commerce'
- 'Dashboards administrativos'
- 'Aplicações com SSR/SSG'
not_suitable_for:
- 'Aplicações mobile-only (use React Native)'
- 'Microsserviços backend puros (use Node.js puro ou NestJS)'
- 'Sites estáticos simples (use Astro)'
```
## Design Patterns (The Essential 5)
> **Critical:** Estes 5 patterns eliminam 95% dos bugs e permitem ao Claude Code trabalhar com máxima eficiência. São complementares e devem ser usados TODOS juntos.
### Pattern 1: Contract Pattern
**Purpose:** Definir APIs públicas entre features para prevenir bugs de integração
**Execution Score:** 10/10 | **Anti-Bug Score:** 10/10
````typescript
// src/features/auth/auth.contract.ts
/**
* Public API for authentication feature
* Other features depend ONLY on this contract, never on implementation
*
* @example
* ```ts
* class CheckoutService {
* constructor(private auth: AuthContract) {}
*
* async process() {
* const user = await this.auth.getCurrentUser()
* }
* }
* ```
*/
export interface AuthContract {
/**
* Authenticate user with credentials
* @throws {InvalidCredentialsError} When credentials are wrong
* @throws {UserBlockedError} When user account is blocked
*/
login(email: string, password: string): Promise<AuthResult>;
/**
* Get currently authenticated user
* @throws {UnauthorizedError} When no user is authenticated
*/
getCurrentUser(): Promise<User>;
/**
* Check if user is authenticated
*/
isAuthenticated(): boolean;
/**
* Sign out current user
*/
logout(): Promise<void>;
}
export type AuthResult = {
user: User;
token: string;
expiresAt: Date;
};
export type User = {
id: string;
email: string;
name: string;
role: 'admin' | 'user';
};
````
**Bugs Eliminated:**
- Feature A expects string, Feature B returns number
- Method renamed breaks all consumers
- Parameters in wrong order
- Unexpected return types
- Circular dependencies between features
**Why Claude Code Excels:**
- TypeScript enforces contracts at compile time
- Claude reads contract (50 lines) instead of implementation (2000 lines)
- Impossible to break contract without TypeScript errors
### Pattern 2: Service Pattern
**Purpose:** Encapsular lógica de negócio em serviços testáveis e reutilizáveis
**Execution Score:** 10/10 | **Anti-Bug Score:** 9/10
```typescript
// src/features/auth/services/auth.service.ts
import { AuthContract, AuthResult, User } from '../auth.contract';
import { UserRepository } from '../repositories/user.repository';
import { EventBus } from '@/shared/events/eventBus';
export class AuthService implements AuthContract {
constructor(
private userRepo: UserRepository,
private eventBus: EventBus
) {}
async login(email: string, password: string): Promise<AuthResult> {
// 1. Validate input
if (!email || !password) {
throw new ValidationError('Email and password are required');
}
// 2. Find user
const user = await this.userRepo.findByEmail(email);
if (!user) {
throw new InvalidCredentialsError('Invalid credentials');
}
// 3. Verify password
const isValid = await this.verifyPassword(password, user.passwordHash);
if (!isValid) {
throw new InvalidCredentialsError('Invalid credentials');
}
// 4. Check if blocked
if (user.isBlocked) {
throw new UserBlockedError('Account is blocked');
}
// 5. Generate token
const token = this.generateToken(user);
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);
// 6. Emit event
this.eventBus.emit('auth:login', user);
return {
user: this.sanitizeUser(user),
token,
expiresAt,
};
}
// ... other methods
}
// Export singleton instance
export const authService = new AuthService(userRepository, eventBus);
```
**Bugs Eliminated:**
- Business logic scattered across components
- Code duplication
- Impossible to test without UI
- Inconsistent error handling
- Side effects in unexpected places
### Pattern 3: Repository Pattern
**Purpose:** Isolar lógica de acesso a dados da lógica de negócio
**Execution Score:** 9/10 | **Anti-Bug Score:** 9/10
```typescript
// src/features/auth/repositories/user.repository.ts
import { db } from '@/lib/database';
export class UserRepository {
async findByEmail(email: string): Promise<User | null> {
return db.user.findUnique({
where: { email },
});
}
async findById(id: string): Promise<User | null> {
return db.user.findUnique({
where: { id },
});
}
async create(data: CreateUserDTO): Promise<User> {
return db.user.create({
data: {
email: data.email,
name: data.name,
passwordHash: data.passwordHash,
role: data.role || 'user',
createdAt: new Date(),
},
});
}
async update(id: string, data: UpdateUserDTO): Promise<User> {
return db.user.update({
where: { id },
data,
});
}
async delete(id: string): Promise<void> {
await db.user.delete({
where: { id },
});
}
}
// Export singleton
export const userRepository = new UserRepository();
```
**Bugs Eliminated:**
- SQL/queries scattered throughout codebase
- Impossible to test without real database
- Changing ORM breaks entire application
- Inconsistent data access patterns
### Pattern 4: Event Bus Pattern (Observer)
**Purpose:** Habilitar acoplamento solto entre features através de arquitetura event-driven
**Execution Score:** 8/10 | **Anti-Bug Score:** 10/10
```typescript
// src/shared/events/eventBus.ts
type EventHandler<T = any> = (data: T) => void | Promise<void>;
export class EventBus {
private handlers = new Map<string, EventHandler[]>();
on<T = any>(event: string, handler: EventHandler<T>): void {
if (!this.handlers.has(event)) {
this.handlers.set(event, []);
}
this.handlers.get(event)!.push(handler);
}
off<T = any>(event: string, handler: EventHandler<T>): void {
const handlers = this.handlers.get(event);
if (handlers) {
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
async emit<T = any>(event: string, data: T): Promise<void> {
const handlers = this.handlers.get(event) || [];
handlers.forEach((handler) => {
try {
Promise.resolve(handler(data)).catch((error) => {
console.error(`Error in handler for event "${event}":`, error);
});
} catch (error) {
console.error(`Error in handler for event "${event}":`, error);
}
});
}
}
export const eventBus = new EventBus();
// Typed events
export type AppEvents = {
'auth:login': User;
'auth:logout': { userId: string };
'order:created': Order;
'order:paid': { orderId: string; amount: number };
};
```
**Usage:**
```typescript
// Feature A emits
this.eventBus.emit('order:created', order);
// Feature B reacts (doesn't know about Feature A)
eventBus.on('order:created', async (order) => {
await emailService.sendOrderConfirmation(order);
});
// Feature C also reacts independently
eventBus.on('order:created', async (order) => {
await analytics.track('purchase', { orderId: order.id });
});
```
**Bugs Eliminated:**
- Circular dependencies between features
- Feature A change breaks Feature B
- Tight coupling makes refactoring impossible
- Adding new functionality requires modifying existing code
### Pattern 5: Builder Pattern (Tests Only)
**Purpose:** Criar fixtures de teste facilmente e consistentemente
**Execution Score:** 10/10 | **Anti-Bug Score:** 8/10
**IMPORTANT:** Use Builders APENAS para TESTES, não em código de produção
```typescript
// test/builders/user.builder.ts
export class UserBuilder {
private data: Partial<User> = {
id: '1',
email: 'test@example.com',
name: 'Test User',
role: 'user',
createdAt: new Date(),
isBlocked: false,
};
withId(id: string): this {
this.data.id = id;
return this;
}
withEmail(email: string): this {
this.data.email = email;
return this;
}
asAdmin(): this {
this.data.role = 'admin';
return this;
}
asBlocked(): this {
this.data.isBlocked = true;
return this;
}
build(): User {
return this.data as User;
}
}
```
**Usage in Tests:**
```typescript
describe('OrderService', () => {
it('should create order for authenticated user', async () => {
const user = new UserBuilder().withEmail('customer@example.com').build();
const admin = new UserBuilder().asAdmin().build();
const blocked = new UserBuilder().asBlocked().build();
// ... test logic
});
});
```
## Project Structure
```
/src
/features # Feature-based organization (NOT type-based)
/auth
/components # UI components specific to auth
/hooks # Custom hooks for auth
/services # Business logic (pure functions)
/repositories # Data access layer
/types # TypeScript types/interfaces
/utils # Helper functions
auth.contract.ts # PUBLIC API (integration point)
index.ts # Barrel export (facade)
/products
[same structure]
/checkout
[same structure]
/_reference # Reference feature (copy good code)
/contracts
/repositories
/services
/hooks
/components
index.ts
/shared # ONLY truly shared code
/components # Reusable UI components
/hooks # Generic hooks
/utils # Generic utilities
/types # Shared types
/events # Event bus
/config # Environment variables, constants
/lib # Third-party integrations
/test
/builders # Test fixture builders
/mocks # MSW handlers and mocks
/e2e # Playwright E2E tests
```
### Structure Rationale
- **Features are self-contained:** Easier to understand context
- **Contract-based integration:** Features communicate via contracts only
- **Reference feature:** Template for new features (copy patterns)
- **Shared is minimal:** Only truly reusable code
## Tech Stack
| Category | Technology | Version | Purpose |
| ------------------- | --------------- | ------- | --------------------------------------- |
| Framework | Next.js | ^16.0.0 | Fullstack React framework (Proxy-based) |
| Language | TypeScript | ^5.0.0 | Type safety |
| Styling | Tailwind CSS | ^3.4.0 | Utility-first CSS |
| UI Components | shadcn/ui | latest | Accessible components |
| State (Global) | Zustand | ^4.5.0 | Simple global state |
| State (Server) | React Query | ^5.0.0 | Server state management |
| Forms | React Hook Form | ^7.50.0 | Form handling |
| Validation | Zod | ^3.22.0 | Schema validation |
| Testing (Unit) | Vitest | ^1.2.0 | Fast unit testing |
| Testing (Component) | Testing Library | ^14.0.0 | Component testing |
| Testing (E2E) | Playwright | ^1.41.0 | E2E testing |
| API Mocking | MSW | ^2.1.0 | Mock Service Worker |
| Database | Prisma | ^5.9.0 | Type-safe ORM |
### Required Dependencies
```bash
# Core
npm install next react react-dom typescript
npm install tailwindcss postcss autoprefixer
npm install @tanstack/react-query zustand
npm install react-hook-form @hookform/resolvers zod
# Dev
npm install -D vitest @testing-library/react @testing-library/jest-dom
npm install -D @playwright/test msw
npm install -D prisma @types/node @types/react
```
## Coding Standards
### Naming Conventions
| Element | Convention | Example |
| ------------ | ---------------------- | ---------------------- |
| Components | PascalCase | `ProductCard.tsx` |
| Hooks | useCamelCase | `useProducts.ts` |
| Services | camelCase + Service | `auth.service.ts` |
| Repositories | camelCase + Repository | `user.repository.ts` |
| Contracts | camelCase + .contract | `auth.contract.ts` |
| Types | PascalCase | `User`, `AuthResult` |
| Constants | SCREAMING_SNAKE | `MAX_ITEMS_PER_PAGE` |
| Tests | _.test.ts or _.spec.ts | `auth.service.test.ts` |
### Critical Rules
1. **Contract Pattern:** Features ONLY expose via `.contract.ts` files
2. **No Cross-Feature Imports:** Import from `@/features/[name]` index only
3. **Types First:** Always define schemas/types BEFORE implementation
4. **Error Handling:** All async operations must have explicit error handling
5. **No `any` Types:** Use `unknown` if type is truly unknown, then narrow
### Next.js 16+ Proxy (substitui Middleware)
> **IMPORTANTE:** Next.js 16 substituiu o Middleware pelo sistema de Proxy. NÃO use `middleware.ts`.
**Antes (Next.js 14 - DEPRECADO):**
```typescript
// middleware.ts - NÃO USE MAIS
export function middleware(request: NextRequest) {
// auth checks, redirects, etc.
}
```
**Agora (Next.js 16+ - USE PROXY):**
```typescript
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
];
},
async redirects() {
return [
{
source: '/old-route',
destination: '/new-route',
permanent: true,
},
];
},
};
export default nextConfig;
```
**Para autenticação, use Server Components ou Route Handlers:**
```typescript
// app/dashboard/page.tsx
import { auth } from '@/features/auth'
import { redirect } from 'next/navigation'
export default async function DashboardPage() {
const session = await auth()
if (!session) redirect('/login')
return <Dashboard user={session.user} />
}
```
**Vantagens do Proxy sobre Middleware:**
- Executa no servidor Node.js (não em Edge Runtime limitado)
- Acesso completo a banco de dados e serviços
- Melhor performance e caching
- Configuração centralizada em `next.config.ts`
### TypeScript Config
```json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true
}
}
```
## Testing Strategy
### Test Pyramid
```
/\
/E2E\ 10% - Critical user flows only
/------\
/Integration\ 20% - Features working together
/------------\
/ Unit Tests \ 70% - Business logic, components
/----------------\
```
### What to Test
#### Always Test (Critical)
- [ ] Business Logic (Services/Utils) - 90%+ coverage
- [ ] Validation & Business Rules
- [ ] Edge Cases (null, empty, max values)
#### Consider Testing
- [ ] Custom Hooks
- [ ] Component Integration
- [ ] API Error Handling
#### Never Test
- [ ] Framework internals (React, Next.js)
- [ ] External libraries (Zod, React Query)
- [ ] Trivial getters/setters
- [ ] CSS/styling
### Coverage Goals
```
- Business logic (services/utils): 90%+
- Hooks: 80%+
- Components: 60%+
- Overall: 70%+
DO NOT pursue 100% - diminishing returns
```
### Test Template
```typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
describe('[Feature]Service', () => {
let service: FeatureService;
let mockRepo: MockType;
beforeEach(() => {
mockRepo = { method: vi.fn() };
service = new FeatureService(mockRepo);
});
describe('methodName', () => {
it('should handle happy path', async () => {
// Arrange
const input = {
/* test data */
};
mockRepo.method.mockResolvedValue({
/* mock response */
});
// Act
const result = await service.methodName(input);
// Assert
expect(result).toEqual({
/* expected */
});
});
it('should handle validation error', async () => {
await expect(service.methodName(invalidInput)).rejects.toThrow('Error message');
});
});
});
```
## Token Economy Strategies
> Estratégias para minimizar consumo de tokens com Claude Code.
### Strategy 1: Show, Don't Tell
```
// BAD: ~1000 tokens explaining patterns
"Create a CheckoutService using the Service pattern with
Dependency Injection following SOLID principles..."
// GOOD: ~300 tokens showing example
"Create CheckoutService following the same pattern as AuthService:
[paste AuthService.ts]"
```
### Strategy 2: Reference Feature
Create one perfect feature as template, then:
```
"Create ProductService identical to _reference/services/reference.service.ts
Just change the entity name and business logic"
```
### Strategy 3: Schemas as Documentation
```typescript
// Schema replaces 50+ lines of explanation
export const registerSchema = z.object({
email: z.string().email(),
password: z
.string()
.min(8)
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/),
name: z.string().min(2).max(100),
});
```
### Strategy 4: Tests as Specifications
```typescript
// Tests are more concise than prose
"Make these tests pass:
describe('calculateShipping', () => {
it('charges R$10 for < 1kg', () => {
expect(calculateShipping(0.5, 50)).toBe(10)
})
it('adds 20% for distance > 100km', () => {
expect(calculateShipping(1, 150)).toBe(12)
})
})"
```
## Bug Prevention Stack
| Layer | Catches | Implementation |
| ------------------ | ------- | -------------------------- |
| TypeScript Strict | 60% | `strict: true` in tsconfig |
| Runtime Validation | 25% | Zod schemas at boundaries |
| Contract Pattern | 10% | Interface enforcement |
| Tests | 5% | Edge cases and regressions |
## Patterns to AVOID
### Singleton Pattern
**Problem:** Hard to test, shared state between tests
```typescript
// BAD
class Database {
private static instance: Database
static getInstance() { ... }
}
// GOOD
export const db = new Database()
```
### TypeScript Decorators
**Problem:** Experimental, confusing syntax, hard to debug
```typescript
// BAD
@Log
@Validate
@Cache
class UserService {}
// GOOD
const userService = rateLimit(cache(validate(new UserService())));
```
### Abstract Factory
**Problem:** Over-engineering for 99% of cases
```typescript
// BAD
abstract class VehicleFactory {
abstract createCar(): Car;
}
// GOOD
const vehicleFactory = {
createCar: (region: string) => (region === 'US' ? new USCar() : new EUCar()),
};
```
## File Templates
### Contract Template
```typescript
// src/features/[feature]/[feature].contract.ts
/**
* Public API for [feature] feature
*/
export interface [Feature]Contract {
/**
* Method description
* @throws {ErrorType} When error occurs
*/
methodName(param: Type): Promise<ReturnType>
}
export type [Feature]Result = {
// type definition
}
export type [Feature]Events = {
'[feature]:event-name': PayloadType
}
```
### Service Template
```typescript
// src/features/[feature]/services/[feature].service.ts
import { [Feature]Contract } from '../[feature].contract'
export class [Feature]Service implements [Feature]Contract {
constructor(
private dependency1: Dependency1Type,
private eventBus: EventBus
) {}
async methodName(param: Type): Promise<ReturnType> {
// 1. Validate
// 2. Execute
// 3. Emit events
// 4. Return
}
}
export const [feature]Service = new [Feature]Service(dep1, eventBus)
```
### Index Template
```typescript
// src/features/[feature]/index.ts
// ONLY export public API
export type { [Feature]Contract, [Feature]Result } from './[feature].contract'
export { [feature]Service } from './services/[feature].service'
// DO NOT export internal implementation details
```
## Integration with AIOS
### Recommended Workflow
1. **Planning Phase:**
- Use `@architect` with `*create-doc fullstack-architecture`
- Reference this preset for patterns and structure
2. **Development Phase:**
- Use `@dev` following the 5 Essential Patterns
- Create features using the Reference Feature strategy
3. **QA Phase:**
- Use `@qa` with the testing strategy defined
- Ensure coverage goals are met
### Related AIOS Templates
- `fullstack-architecture-tmpl.yaml` - Main architecture document
- `front-end-architecture-tmpl.yaml` - Frontend specifics
- `story-tmpl.yaml` - User story format
### AIOS Commands
```bash
# Create architecture doc using this preset
@architect *create-doc fullstack-architecture
# Reference this preset
@dev "Follow the nextjs-react preset patterns"
```
## Checklist for New Features
```markdown
When creating a new feature:
- [ ] Define Contract if feature will be used by others
- [ ] Create Repository for all data access
- [ ] Implement Service for business logic
- [ ] Use Event Bus for cross-feature communication
- [ ] Create Builders for test fixtures
- [ ] Export only public API through index.ts
- [ ] Write tests using mocked contracts
- [ ] Document integration points
When integrating with existing feature:
- [ ] Import ONLY the Contract, never implementation
- [ ] Use Event Bus if don't need synchronous response
- [ ] Mock contracts in tests
- [ ] Don't create circular dependencies
```
## Changelog
| Date | Version | Changes |
| ---------- | ------- | ---------------------------------------------------- |
| 2026-01-28 | 1.1.0 | Update to Next.js 16+, replace Middleware with Proxy |
| 2025-01-27 | 1.0.0 | Initial version based on DEVELOPMENT_GUIDE.md |
_AIOS Tech Preset - Synkra AIOS Framework_