feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS: - next-best-practices v0.1.0 (CLEAN) — Next.js App Router, RSC, caching, data - nextjs-patterns v1.0.0 (CLEAN) — Next.js 15: Server Actions, route handlers - vite v1.0.0 (CLEAN) — env vars, aliases, proxy, CJS compat - uncle-bob v1.0.0 (CLEAN) — Clean Code, SOLID, Clean Architecture - clean-code-review v1.0.0 (CLEAN) — naming, guard clauses, anti-patterns, refactoring - vue v1.0.0 (CLEAN) — Vue framework - vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos): - typescript/ — TS safe + generics gotchas - react/ — Next.js App Router + Vite config - vue/ — Composition API + Pinia - linux/ — System diagnostic cheatsheet - database/ — PostgreSQL + MySQL patterns - browser/ — Chromium CLI + E2E testing - security/ — SAST audit (OWASP Top 10) - best-practices/ — Clean Code + SOLID + Clean Architecture - deploy/ — Docker multi-stack + OpenClaw ops - + INDEX.md como guia de navegacao .learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
# Clean Architecture — Detailed Guide
|
||||
|
||||
## The Core Idea
|
||||
|
||||
Separate the software into layers. Each layer has a specific role. Dependencies point inward.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Frameworks & Drivers │ ← DB, Web, UI, devices
|
||||
│ ┌────────────────────────────────────┐ │
|
||||
│ │ Interface Adapters │ │ ← Controllers, Gateways, Presenters
|
||||
│ │ ┌──────────────────────────────┐ │ │
|
||||
│ │ │ Use Cases │ │ │ ← Application business rules
|
||||
│ │ │ ┌────────────────────────┐ │ │ │
|
||||
│ │ │ │ Entities │ │ │ │ ← Enterprise business rules
|
||||
│ │ │ └────────────────────────┘ │ │ │
|
||||
│ │ └──────────────────────────────┘ │ │
|
||||
│ └────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## The Dependency Rule
|
||||
|
||||
Source code dependencies must only point **inward**. Nothing in an inner ring can know anything about an outer ring. This includes functions, classes, variables, types, or any named entity.
|
||||
|
||||
## Layer Details
|
||||
|
||||
### Entities (Innermost)
|
||||
|
||||
- Encapsulate enterprise-wide business rules.
|
||||
- Could be used by many applications in the enterprise.
|
||||
- Least likely to change when something external changes.
|
||||
- Pure domain objects with business logic. No framework dependencies.
|
||||
|
||||
```typescript
|
||||
// Pure entity — no imports from outer layers
|
||||
class Account {
|
||||
constructor(
|
||||
readonly id: string,
|
||||
private balance: number,
|
||||
) {}
|
||||
|
||||
deposit(amount: number) {
|
||||
if (amount <= 0) throw new DomainError('Amount must be positive')
|
||||
this.balance += amount
|
||||
}
|
||||
|
||||
withdraw(amount: number) {
|
||||
if (amount > this.balance) throw new InsufficientFundsError()
|
||||
this.balance -= amount
|
||||
}
|
||||
|
||||
getBalance() { return this.balance }
|
||||
}
|
||||
```
|
||||
|
||||
### Use Cases
|
||||
|
||||
- Application-specific business rules.
|
||||
- Orchestrate the flow of data to and from entities.
|
||||
- Direct entities to use their enterprise-wide business rules.
|
||||
- Changes to this layer should not affect entities.
|
||||
- Changes to external layers (DB, UI) should not affect use cases.
|
||||
|
||||
```typescript
|
||||
// Use case — depends on entities and port interfaces, nothing else
|
||||
class TransferFundsUseCase {
|
||||
constructor(
|
||||
private accountRepo: AccountRepository, // Port (interface)
|
||||
private notifier: TransferNotifier, // Port (interface)
|
||||
) {}
|
||||
|
||||
async execute(fromId: string, toId: string, amount: number) {
|
||||
const from = await this.accountRepo.findById(fromId)
|
||||
const to = await this.accountRepo.findById(toId)
|
||||
|
||||
from.withdraw(amount)
|
||||
to.deposit(amount)
|
||||
|
||||
await this.accountRepo.save(from)
|
||||
await this.accountRepo.save(to)
|
||||
await this.notifier.notify(fromId, toId, amount)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Interface Adapters
|
||||
|
||||
- Convert data between the format most convenient for use cases/entities and the format most convenient for external things (DB, web).
|
||||
- Controllers, presenters, gateways live here.
|
||||
- No business logic — only translation.
|
||||
|
||||
```typescript
|
||||
// Controller (adapter) — converts HTTP to use case input
|
||||
class TransferController {
|
||||
constructor(private useCase: TransferFundsUseCase) {}
|
||||
|
||||
async handle(req: HttpRequest): Promise<HttpResponse> {
|
||||
const { fromId, toId, amount } = req.body
|
||||
await this.useCase.execute(fromId, toId, amount)
|
||||
return { status: 200, body: { success: true } }
|
||||
}
|
||||
}
|
||||
|
||||
// Repository implementation (adapter) — converts use case port to DB
|
||||
class PostgresAccountRepository implements AccountRepository {
|
||||
async findById(id: string): Promise<Account> {
|
||||
const row = await this.db.query('SELECT * FROM accounts WHERE id = $1', [id])
|
||||
return new Account(row.id, row.balance)
|
||||
}
|
||||
|
||||
async save(account: Account): Promise<void> {
|
||||
await this.db.query('UPDATE accounts SET balance = $1 WHERE id = $2',
|
||||
[account.getBalance(), account.id])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Frameworks & Drivers (Outermost)
|
||||
|
||||
- Glue code. Minimal.
|
||||
- Web framework config, database drivers, HTTP server setup.
|
||||
- This is where all the details go. Keep them out of the inner circles.
|
||||
|
||||
## Ports and Adapters (Hexagonal Architecture)
|
||||
|
||||
Clean Architecture is compatible with hexagonal architecture:
|
||||
|
||||
- **Ports**: interfaces defined by the use case layer (what it needs from the outside).
|
||||
- **Adapters**: implementations in the outer layer that fulfill ports.
|
||||
|
||||
```typescript
|
||||
// PORT — defined in use case layer
|
||||
interface AccountRepository {
|
||||
findById(id: string): Promise<Account>
|
||||
save(account: Account): Promise<void>
|
||||
}
|
||||
|
||||
// ADAPTER — defined in infrastructure layer
|
||||
class DrizzleAccountRepository implements AccountRepository {
|
||||
// Implementation using Drizzle ORM
|
||||
}
|
||||
```
|
||||
|
||||
## Crossing Boundaries
|
||||
|
||||
When data crosses a boundary, it should be in the form most convenient for the **inner** circle. Never pass database rows or HTTP request objects into use cases.
|
||||
|
||||
Use simple DTOs or value objects:
|
||||
|
||||
```typescript
|
||||
// Input DTO for use case
|
||||
interface TransferInput {
|
||||
fromAccountId: string
|
||||
toAccountId: string
|
||||
amount: number
|
||||
}
|
||||
|
||||
// Output DTO from use case
|
||||
interface TransferResult {
|
||||
success: boolean
|
||||
newBalance: number
|
||||
}
|
||||
```
|
||||
|
||||
## The Composition Root
|
||||
|
||||
All dependency wiring happens at the outermost layer — the "main" or "composition root":
|
||||
|
||||
```typescript
|
||||
// main.ts — the only place that knows about ALL concrete implementations
|
||||
const db = new PostgresDatabase(config.dbUrl)
|
||||
const accountRepo = new PostgresAccountRepository(db)
|
||||
const notifier = new EmailTransferNotifier(config.smtp)
|
||||
const transferUseCase = new TransferFundsUseCase(accountRepo, notifier)
|
||||
const controller = new TransferController(transferUseCase)
|
||||
|
||||
app.post('/transfer', (req, res) => controller.handle(req, res))
|
||||
```
|
||||
|
||||
## Testing Benefits
|
||||
|
||||
Each layer can be tested independently:
|
||||
|
||||
- **Entities**: pure unit tests, no mocks needed.
|
||||
- **Use Cases**: mock the ports (repositories, services).
|
||||
- **Adapters**: integration tests against real infrastructure.
|
||||
- **End-to-end**: full stack through the composition root.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Letting entities import from frameworks (ORM decorators on domain objects).
|
||||
- Putting business logic in controllers.
|
||||
- Use cases that know about HTTP status codes or database queries.
|
||||
- Skipping the adapter layer and having use cases talk directly to the DB.
|
||||
- Over-engineering: not every project needs all four layers. Scale the architecture to the complexity.
|
||||
|
||||
## Pragmatic Application
|
||||
|
||||
- Start with two layers (domain + infrastructure) for small projects.
|
||||
- Add use case and adapter layers as complexity grows.
|
||||
- The dependency rule is the non-negotiable part. Everything else is negotiable.
|
||||
- Frameworks are details. Design your system so switching a framework is possible (even if you never do).
|
||||
@@ -0,0 +1,210 @@
|
||||
# SOLID Principles — Detailed Guide
|
||||
|
||||
## S — Single Responsibility Principle (SRP)
|
||||
|
||||
> "A module should have one, and only one, reason to change."
|
||||
|
||||
More precisely: a module should be responsible to one, and only one, actor (stakeholder).
|
||||
|
||||
### Violation
|
||||
|
||||
```typescript
|
||||
class Employee {
|
||||
calculatePay() // CFO's team cares about this
|
||||
reportHours() // COO's team cares about this
|
||||
save() // CTO's team cares about this
|
||||
}
|
||||
```
|
||||
|
||||
Three actors, three reasons to change. A change for payroll could break hour reporting.
|
||||
|
||||
### Fix
|
||||
|
||||
Separate into three classes, each responsible to one actor. Use a facade if you need a single entry point.
|
||||
|
||||
```typescript
|
||||
class PayCalculator { calculatePay(employee: Employee) {} }
|
||||
class HourReporter { reportHours(employee: Employee) {} }
|
||||
class EmployeeSaver { save(employee: Employee) {} }
|
||||
```
|
||||
|
||||
### Heuristic
|
||||
|
||||
If you describe a class and use "and" — it probably has multiple responsibilities.
|
||||
|
||||
---
|
||||
|
||||
## O — Open/Closed Principle (OCP)
|
||||
|
||||
> "Software entities should be open for extension, closed for modification."
|
||||
|
||||
Add new behavior by adding new code, not changing existing code.
|
||||
|
||||
### Violation
|
||||
|
||||
```typescript
|
||||
function calculateArea(shape: Shape) {
|
||||
if (shape.type === 'circle') return Math.PI * shape.radius ** 2
|
||||
if (shape.type === 'rectangle') return shape.width * shape.height
|
||||
// Every new shape = modify this function
|
||||
}
|
||||
```
|
||||
|
||||
### Fix
|
||||
|
||||
Use polymorphism:
|
||||
|
||||
```typescript
|
||||
interface Shape { area(): number }
|
||||
|
||||
class Circle implements Shape {
|
||||
constructor(private radius: number) {}
|
||||
area() { return Math.PI * this.radius ** 2 }
|
||||
}
|
||||
|
||||
class Rectangle implements Shape {
|
||||
constructor(private width: number, private height: number) {}
|
||||
area() { return this.width * this.height }
|
||||
}
|
||||
```
|
||||
|
||||
New shapes extend the system without modifying `calculateArea`.
|
||||
|
||||
### Heuristic
|
||||
|
||||
If adding a feature requires modifying a switch/case or if-else chain, consider OCP.
|
||||
|
||||
---
|
||||
|
||||
## L — Liskov Substitution Principle (LSP)
|
||||
|
||||
> "Subtypes must be substitutable for their base types."
|
||||
|
||||
If `S` extends `T`, anywhere you use `T` you should be able to use `S` without surprises.
|
||||
|
||||
### Classic Violation: Square/Rectangle
|
||||
|
||||
```typescript
|
||||
class Rectangle {
|
||||
setWidth(w: number) { this.width = w }
|
||||
setHeight(h: number) { this.height = h }
|
||||
}
|
||||
|
||||
class Square extends Rectangle {
|
||||
setWidth(w: number) { this.width = w; this.height = w }
|
||||
setHeight(h: number) { this.width = h; this.height = h }
|
||||
}
|
||||
|
||||
// Breaks expectations:
|
||||
function resize(r: Rectangle) {
|
||||
r.setWidth(5)
|
||||
r.setHeight(10)
|
||||
assert(r.area() === 50) // Fails for Square!
|
||||
}
|
||||
```
|
||||
|
||||
### Fix
|
||||
|
||||
Don't model Square as a subtype of Rectangle. Use composition or separate types.
|
||||
|
||||
### Heuristic
|
||||
|
||||
If a subclass overrides a method to do something the caller wouldn't expect, it violates LSP.
|
||||
|
||||
---
|
||||
|
||||
## I — Interface Segregation Principle (ISP)
|
||||
|
||||
> "Clients should not be forced to depend on methods they don't use."
|
||||
|
||||
### Violation
|
||||
|
||||
```typescript
|
||||
interface Worker {
|
||||
work(): void
|
||||
eat(): void
|
||||
sleep(): void
|
||||
}
|
||||
|
||||
// A Robot worker doesn't eat or sleep
|
||||
class Robot implements Worker {
|
||||
work() { /* ... */ }
|
||||
eat() { throw new Error('Robots do not eat') }
|
||||
sleep() { throw new Error('Robots do not sleep') }
|
||||
}
|
||||
```
|
||||
|
||||
### Fix
|
||||
|
||||
Split into focused interfaces:
|
||||
|
||||
```typescript
|
||||
interface Workable { work(): void }
|
||||
interface Feedable { eat(): void }
|
||||
interface Restable { sleep(): void }
|
||||
|
||||
class Human implements Workable, Feedable, Restable { /* ... */ }
|
||||
class Robot implements Workable { /* ... */ }
|
||||
```
|
||||
|
||||
### Heuristic
|
||||
|
||||
If implementing an interface forces you to write empty methods or throw "not supported", the interface is too fat.
|
||||
|
||||
---
|
||||
|
||||
## D — Dependency Inversion Principle (DIP)
|
||||
|
||||
> "Depend on abstractions, not concretions."
|
||||
|
||||
High-level modules (policy) must not depend on low-level modules (details). Both should depend on abstractions.
|
||||
|
||||
### Violation
|
||||
|
||||
```typescript
|
||||
class OrderService {
|
||||
private db = new PostgresDatabase() // Concrete dependency
|
||||
|
||||
createOrder(order: Order) {
|
||||
this.db.insert('orders', order)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fix
|
||||
|
||||
Depend on an abstraction; inject the implementation:
|
||||
|
||||
```typescript
|
||||
interface OrderRepository {
|
||||
save(order: Order): Promise<void>
|
||||
}
|
||||
|
||||
class OrderService {
|
||||
constructor(private repository: OrderRepository) {}
|
||||
|
||||
createOrder(order: Order) {
|
||||
this.repository.save(order)
|
||||
}
|
||||
}
|
||||
|
||||
// Inject at composition root:
|
||||
const service = new OrderService(new PostgresOrderRepository())
|
||||
```
|
||||
|
||||
### Heuristic
|
||||
|
||||
If a class instantiates its own dependencies with `new`, it's likely violating DIP. Inject dependencies through the constructor.
|
||||
|
||||
---
|
||||
|
||||
## Applying SOLID Together
|
||||
|
||||
These principles reinforce each other:
|
||||
|
||||
- SRP keeps classes focused → easier to apply OCP
|
||||
- OCP uses polymorphism → requires LSP-compliant subtypes
|
||||
- ISP keeps interfaces thin → makes DIP practical
|
||||
- DIP enables testing → which validates LSP
|
||||
|
||||
Don't apply them dogmatically. They're tools for managing complexity. A simple script doesn't need SOLID. A growing system does.
|
||||
Reference in New Issue
Block a user