Teu progresso
0 / 83 módulos0%
Estágio 04 · 04-08
BloqueadoDecidir entre monolito, microservices e serverless é a decisão que mais frequentemente é tomada por moda em vez de necessidade. Em 2014, "microservices everywhere". Em 2020, "monolith first". Em 2026, "modular monolith + targeted services + edge functions". A verdade não muda: arquitetura serve ao contexto (time, domain, scale, ops capacity, business stage).
Este módulo é o framework de decisão. Quando dividir, quando juntar, e como reverter erro. Custo operacional real de cada modelo, não apenas o discurso de talks.
1 deployable, 1 codebase, geralmente 1 DB.
Pros:
Cons:
Monolith + module boundaries fortes:
Default em 2026 pra projetos médios. Maioria das vantagens monolith + facilita future split.
N services independentes:
Pros:
Cons:
Function-as-a-Service. Lambda, Cloud Run, Cloudflare Workers.
Pros:
Cons:
| Critério | Monolith | Modular Mon | Microservices | Serverless |
|---|---|---|---|---|
| Time | 1-3 devs | 3-15 | 15+ | qualquer |
| Domain complexity | low-med | med-high | high | low-med |
| Traffic | low-med | med-high | high | spiky/low |
| Ops capacity | low | med | high | very low |
| Stack diversity | low | low | high | low-med |
| Latency budget | tight | tight | flexible | flexible |
| Vendor lock-in concern | low | low | low | high |
Use isso pra orientar conversa, não regra absoluta.
Sam Newman: comece monolith, extract services quando dor justifica.
Razões:
Anti-padrão: greenfield com microservices "preparando pra escala". Você não tem usuário ainda.
Refactor monolith → microservices:
Martin Fowler popularizou. Padrão pra migração segura.
"Microservice" não significa "tiny". Tamanho:
"Nanoservices" (1 endpoint por service) é anti-pattern: operação domina valor.
Padrão microservices: each service owns DB próprio. Cross-service via API/events.
Pros: encapsulation, evolução independente. Cons: cross-service queries impossíveis em SQL; reporting via materialized views ou data warehouse.
Em modular monolith: 1 DB com schemas separados pode trabalhar (Postgres schemas), mantendo isolation.
Cross-service ACID = sagas (04-03). Não 2PC.
Trade-off: complexity exposta. Em monolith, transação local resolve.
Critério pra dividir: você tolera eventual consistency entre esses contexts? Se não, mantenha juntos.
Microservices em escala precisam:
Sem platform, microservices = chaos.
Vince Vance: "serverless first" pra alguns workloads:
Anti-padrão: backend principal de SaaS sério em Lambda. Cold start, conn pool problems, cost em volume.
Variant serverless: roda em edge (Cloudflare Workers, Vercel Edge). Zero cold start (V8 isolates), distribuído globalmente.
Vence em:
Limites: bundle 1MB+, sem long-lived state, sem Node APIs completas.
EDA (04-03) é ortogonal a monolith vs microservices. Você pode ter:
Domain events permitem extrair service depois sem mudar emissor.
"Organizations design systems mirroring communication structures."
Operacional:
Em 2026, projetos pequenos NÃO devem fazer microservices.
orders + users). Consistency via API replaying = teatro.RequestResponse. Cascading timeouts, custo dobrado/triplicado (cobra invocação + tempo do callee bloqueando caller), debug horrível. Use Step Functions ou EventBridge.Logística é multi-tenant (lojistas isolados). Decisão arquitetural ortogonal a "monolith vs services": 3 modelos de isolation, com consequências em custo, blast radius e compliance.
| Modelo | Isolation | Custo | Quando |
|---|---|---|---|
| Pool (shared) | tenant_id discriminator + Postgres RLS | Baixo | SMB SaaS, milhares de tenants pequenos |
| Bridge (silo parcial) | Schema-per-tenant no mesmo DB | Médio | Mid-market, customização leve por tenant |
| Silo (full) | DB / cluster / VPC dedicado | Alto | Enterprise, regulatório (HIPAA, on-prem) |
Pool com Row-Level Security:
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::uuid);
Cada conexão seta app.tenant_id no início; queries filtram automaticamente. Vantagem: impossível "esquecer o WHERE tenant_id". Limite: queries cross-tenant (analytics admin) precisam role privilegiado bypassando RLS.
Noisy neighbor é o demônio do pool: 1 tenant gigante lockando tabela ou esgotando connection pool degrada todos. Mitigação:
tenant_id em Redis — ver 04-04).Custos cross-cutting que aparecem em multi-tenant escalado:
Game day obrigatório no v3 de Logística: derrubar 1 tenant fictício de propósito (consumir 100% do pool, lockar tabela, gerar 10x throughput). Confirmar que demais tenants permanecem dentro do SLO. Se não permanecem, multi-tenancy é teatro.
Cruza com 02-09 (RLS), 04-04 (bulkheads, rate limit), 04-09 (sharding by tenant), 03-15 (per-tenant SLO).
Perguntas:
Default: comece monolith modular. Extract services quando dor real (não imaginária) bate.
Critérios objetivos pra extrair serviço (todos, não 1 só):
Sem 3+ desses, modular monolith é ROI superior.
Distributed transaction (2PC) é morta em microservices — performance ruim, dependência hard de coordinator, locks distribuídos travam o sistema sob carga. Saga é o substituto: long-running transaction modelada como sequência de local transactions com compensações em caso de falha. Duas variantes — choreography (event-driven) e orchestration (central coordinator) — com trade-offs claros. Temporal/Cadence/Step Functions são as runtimes 2026 production-ready pra durable workflow.
Foundation: o problema concreto
PlaceOrder envolve:
1. ReserveInventory (Inventory service)
2. ChargePayment (Payment service)
3. AssignCourier (Dispatch service)
4. SendNotification (Notification service)
Se step 3 falha, precisa:
- Refund payment (compensate step 2)
- Release inventory (compensate step 1)
Choreography (event-driven)
Cada serviço escuta events e emite events; sem coordinator central.
API → emite OrderRequested
Inventory consumer → reserva → emite InventoryReserved | InventoryFailed
Payment consumer (escuta InventoryReserved) → cobra → emite PaymentCharged | PaymentFailed
Dispatch consumer (escuta PaymentCharged) → assign → emite CourierAssigned | DispatchFailed
Notification consumer (escuta CourierAssigned) → send
Se DispatchFailed:
Payment consumer escuta DispatchFailed → refund → emite PaymentRefunded
Inventory consumer escuta PaymentRefunded → release → emite InventoryReleased
// Inventory consumer (Kafka)
await consumer.run({
eachMessage: async ({ topic, message }) => {
const event = JSON.parse(message.value!.toString());
if (topic === 'order-events' && event.type === 'OrderRequested') {
try {
await reserveInventory(event.orderId, event.items);
await producer.send({
topic: 'order-events',
messages: [{ value: JSON.stringify({ type: 'InventoryReserved', orderId: event.orderId, sagaId: event.sagaId }) }],
});
} catch (reason) {
await producer.send({
topic: 'order-events',
messages: [{ value: JSON.stringify({ type: 'InventoryFailed', orderId: event.orderId, sagaId: event.sagaId, reason: String(reason) }) }],
});
}
}
if (event.type === 'PaymentRefunded') {
await releaseInventory(event.orderId);
await producer.send({
topic: 'order-events',
messages: [{ value: JSON.stringify({ type: 'InventoryReleased', orderId: event.orderId, sagaId: event.sagaId }) }],
});
}
},
});
Orchestration (central coordinator)
Um Saga Orchestrator é serviço que invoca steps explicitamente e compensações.
class PlaceOrderSaga {
async execute(input: { orderId: string; items: Item[]; userId: string }) {
const compensations: Array<() => Promise<void>> = [];
try {
await inventory.reserve(input.orderId, input.items);
compensations.unshift(() => inventory.release(input.orderId));
const charge = await payment.charge(input.userId, computeTotal(input.items));
compensations.unshift(() => payment.refund(charge.id));
const assignment = await dispatch.assignCourier(input.orderId);
compensations.unshift(() => dispatch.unassignCourier(assignment.id));
await notification.send(input.userId, { type: 'OrderConfirmed', orderId: input.orderId });
return { success: true };
} catch (err) {
for (const compensate of compensations) {
try {
await compensate();
} catch (compErr) {
alertOps({ sagaFailed: true, compErr, originalErr: err });
}
}
throw err;
}
}
}
Temporal/Cadence — saga como código durable
// Workflow definition (Temporal SDK)
import { proxyActivities, ApplicationFailure, workflowInfo } from '@temporalio/workflow';
import type * as activities from './activities';
const { reserveInventory, releaseInventory, chargePayment, refundPayment, assignCourier, unassignCourier, sendNotification } =
proxyActivities<typeof activities>({
startToCloseTimeout: '30 seconds',
retry: { maximumAttempts: 3 },
});
export async function placeOrderWorkflow(input: PlaceOrderInput): Promise<PlaceOrderResult> {
const compensations: Array<() => Promise<void>> = [];
try {
await reserveInventory(input.orderId, input.items);
compensations.unshift(() => releaseInventory(input.orderId));
const charge = await chargePayment(input.userId, computeTotal(input.items));
compensations.unshift(() => refundPayment(charge.id));
const assignment = await assignCourier(input.orderId);
compensations.unshift(() => unassignCourier(assignment.id));
await sendNotification(input.userId, { type: 'OrderConfirmed' });
return { sagaId: workflowInfo().workflowId, status: 'completed' };
} catch (err) {
for (const c of compensations) {
await c(); // Temporal retries automatic
}
throw ApplicationFailure.create({ message: 'Saga compensated', type: 'SagaFailed' });
}
}
Date.now() direto, no I/O).Compensating transactions — design rules
State machine vs free-form workflow
Choreography vs orchestration — decisão pragmática
| Critério | Choreography | Orchestration |
|---|---|---|
| Steps independentes | Sim | OK |
| Debug facility | Hard | Easy |
| Time to onboard new dev | Slow (precisa entender event flow) | Fast (1 service) |
| Adicionar new step | Edita N consumers | Edita 1 orchestrator |
| Performance (latency end-to-end) | Igual ou melhor (parallel possível) | Sequencial por default |
| Fault tolerance | Excelente (loosely coupled) | Boa, mas orchestrator é crítico |
| When | 3-5 services com clear ownership | > 5 services ou business logic complexa |
Logística decision
Anti-patterns observados
sagaId em event headers + log fields.Tooling 2026
Cruza com 04-08 §2.14 (event-driven escolha), 04-02 §2.18 (idempotent consumer é fundação), 04-03 §2.8 (outbox pattern alimenta choreography), 04-04 (resilience patterns aplicam a saga steps), 03-15 (incident response em saga stuck).
Strangler Fig pattern (Martin Fowler, 2004). Nome vem do Strangler Fig vine — cipó que cresce ao redor da árvore hospedeira até substituí-la. Novo serviço roda lado a lado com legacy; funcionalidade migra gradualmente; legacy é retirado no final. Big-bang rewrites falham (~70% taxa de fracasso citada por Joel Spolsky em "Things You Should Never Do"); migração incremental é a única opção segura. Use cases: monolith → microservices, legacy stack → modern, vendor migration (Heroku → Railway, on-prem → cloud).
Anatomia das 5 fases
Phase 1 — Façade pattern
Routing-only, zero business logic. Tools 2026: nginx, Caddy, AWS API Gateway, Cloudflare Workers, Envoy.
# nginx façade routing
upstream legacy_monolith { server legacy.internal:8080; }
upstream orders_service { server orders.internal:3000; }
server {
listen 80;
# New service handles /orders/* (subset)
location /api/orders/ { proxy_pass http://orders_service; }
# Legacy still handles everything else
location /api/ { proxy_pass http://legacy_monolith; }
}
Legacy não sofre alteração nenhuma — façade é puro roteamento. Permite rollback instantâneo (flip route back).
Phase 2 — Extract com dual-write
Legacy escreve no próprio DB E publica evento / chama new service. Eventual consistency (lag pequeno legacy ↔ new) é aceitável. Better: outbox pattern (cruza com 04-02 §2.18) pra dual-write confiável.
// In legacy monolith — naive dual-write
async function createOrderLegacy(data: OrderData) {
const tx = await db.transaction();
try {
const order = await tx.orders.insert(data);
// Dual-write to new service (substituir por outbox em prod)
await fetch('http://orders.internal/orders', {
method: 'POST',
body: JSON.stringify(order),
});
await tx.commit();
return order;
} catch (e) {
await tx.rollback();
throw e;
}
}
Phase 3 — Verify (shadow + canary + diff)
Shadow traffic: copia request pro new service, ignora response. Canary: 1-5% de tráfego real, monitora errors + latency. Diff testing: compara responses legacy vs new pro mesmo input.
// Shadow request middleware (fire-and-forget)
app.use(async (req, res, next) => {
newOrdersService
.fetch(req.url, req.method, req.body)
.catch((e) => log.warn('shadow failed', e));
next();
});
Tool: GitHub Scientist (Ruby; ports Node node-scientist) codifica "old vs new" diff testing — executa ambos, retorna old, loga divergências.
Phase 4 — Cutover
Option A — Big bang: flip façade pra 100% new service, rollback pronto. Option B — Gradual: 10% → 25% → 50% → 100% via traffic split. Argo Rollouts (cobre 03-04 §2.20) pra K8s gradual cutover. Validar: error rate, p95/p99 latency, business metrics (orders/min unchanged).
Phase 5 — Retire
Wait period 2-4 semanas após cutover antes de deletar legacy code (rollback safety). Code archaeology: identificar todos entry points, deletar tests, remover do build. Database: legacy tables read-only primeiro; deprecation notice; eventually drop. Erro comum: deixar legacy rodando "just in case" pra sempre — dead weight + security risk.
Anti-corruption Layer (ACL) (cruza com 04-06 §2.18)
New service não deve herdar legacy schema warts. ACL traduz entre legacy data model e new domain model. Logística — legacy tbl_pedido (Portuguese, snake_case, denormalized) → new Order aggregate:
// ACL — adapter pattern
class LegacyOrderAdapter {
static fromLegacy(row: TblPedidoRow): Order {
return new Order({
id: row.cod_pedido,
customerId: row.cod_cliente,
items: this.parseItems(row.itens_json),
status: this.mapStatus(row.status_str),
createdAt: new Date(row.dt_criacao),
});
}
static toLegacy(order: Order): TblPedidoRow {
return {
cod_pedido: order.id,
cod_cliente: order.customerId,
itens_json: JSON.stringify(order.items),
status_str: this.unmapStatus(order.status),
dt_criacao: order.createdAt.toISOString(),
};
}
private static mapStatus(s: string): OrderStatus {
const map: Record<string, OrderStatus> = {
PEND: 'placed',
EM_TRANS: 'in_transit',
ENT: 'delivered',
CANC: 'cancelled',
};
return map[s] ?? 'unknown';
}
private static unmapStatus(s: OrderStatus): string {
const inv: Record<OrderStatus, string> = {
placed: 'PEND',
in_transit: 'EM_TRANS',
delivered: 'ENT',
cancelled: 'CANC',
unknown: 'PEND',
};
return inv[s];
}
}
ACL bidirecional (fromLegacy + toLegacy) é obrigatório quando dual-write está ativo.
Database migration patterns
Logística applied — extracting orders from monolith
/api/orders/* (read-only) pra new service mock.OrdersController do monolith.Anti-patterns observados
Tooling 2026
node-scientist, scientist-py.outbox_events.Cruza com 04-06 §2.18 (DDD ACL), 04-07 (architectures), 04-02 §2.18 (outbox pattern), 04-13 §2.18 (CDC sync), 03-04 §2.20 (CI/CD canary), 02-09 §2.21 (Postgres logical replication).
§2.13 introduziu edge functions como conceito (deploy próximo do usuário, latência sub-50ms global). §2.22 desce no mecanismo: por que V8 isolates substituem containers, qual o custo real de cold start, o que cada runtime (Cloudflare Workers, Vercel Edge, Deno Deploy, Bun) entrega em 2026, e quando edge perde pra Lambda/container tradicional.
Edge ≠ serverless full. Lambda roda Node/Python/Go inteiro em micro-VM (Firecracker) com cold start 100-1000ms, 10GB RAM, 15min execution, 250MB unzipped deps. Edge runtime roda subset de Web Standards APIs num isolate compartilhado com cold start ~5ms, 128MB RAM típico, 30s CPU max, 10MB script comprimido. Constraints diferentes ⇒ casos de uso diferentes.
V8 isolate model (Cloudflare Workers):
Workers não usa container per-request. Um único processo V8 hospeda milhares de isolates simultâneos — cada isolate é um sandbox JS com heap próprio, mas compartilha o processo, evitando overhead de fork/exec. Segurança: SES (Secure ECMAScript), capability-based isolation, no shared memory entre isolates. Cold start ~5ms (apenas instanciar V8 isolate + carregar script JS). Lambda Node cold start: 200-1000ms (boot Firecracker microVM + Node runtime + carregar deps + init handler).
┌─────────────────────────────────────────────────┐
│ V8 Process (1 worker node) │
│ ├─ Isolate A (script tenant1) heap: 8MB │
│ ├─ Isolate B (script tenant2) heap: 12MB │
│ ├─ Isolate C (script tenant3) heap: 4MB │
│ └─ ... (milhares simultâneos) │
└─────────────────────────────────────────────────┘
↑ shared V8 = startup ~5ms
vs Lambda:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ microVM 1 │ │ microVM 2 │ │ microVM 3 │
│ Node + deps │ │ Node + deps │ │ Node + deps │
│ 200-1000ms │ │ 200-1000ms │ │ 200-1000ms │
└──────────────┘ └──────────────┘ └──────────────┘
Cloudflare Workers stack 2026:
fetch, Request, Response, crypto.subtle, WebSocket). nodejs_compat flag habilita subset do Node API (node:buffer, node:crypto, node:fs/promises parcial). Paid plan: 30s CPU time (era 50ms em 2023, mudou em 2024), 5min wallclock com WAITUNTIL, 128MB RAM. Free: 10ms CPU.Cloudflare Worker com Hono + Durable Object + Hyperdrive:
// src/worker.ts
import { Hono } from "hono";
import postgres from "postgres";
type Env = {
DB: Hyperdrive; // binding pra Postgres central via Hyperdrive
COURIER_LOCATION: DurableObjectNamespace; // single-instance state
KV: KVNamespace;
AI: Ai; // Workers AI
};
const app = new Hono<{ Bindings: Env }>();
app.get("/courier/:id/location", async (c) => {
const id = c.req.param("id");
// Durable Object: 1 instance por courier, websocket subscribers conectam todos no mesmo lugar
const stub = c.env.COURIER_LOCATION.get(c.env.COURIER_LOCATION.idFromName(id));
return stub.fetch(c.req.raw);
});
app.get("/orders/:id", async (c) => {
const sql = postgres(c.env.DB.connectionString, { max: 5 }); // pooled via Hyperdrive
const [order] = await sql`SELECT * FROM orders WHERE id = ${c.req.param("id")} LIMIT 1`;
c.executionCtx.waitUntil(sql.end()); // não bloqueia response
return c.json(order);
});
export default app;
// Durable Object: state global single-instance
export class CourierLocation {
constructor(private state: DurableObjectState, private env: Env) {}
async fetch(req: Request): Promise<Response> {
if (req.headers.get("Upgrade") === "websocket") {
const [client, server] = Object.values(new WebSocketPair());
this.state.acceptWebSocket(server); // hibernation API: WS sobrevive a DO restart
return new Response(null, { status: 101, webSocket: client });
}
const lastLoc = await this.state.storage.get<{ lat: number; lng: number }>("loc");
return Response.json(lastLoc ?? { lat: 0, lng: 0 });
}
async webSocketMessage(ws: WebSocket, msg: string) {
const loc = JSON.parse(msg);
await this.state.storage.put("loc", loc); // transacional, persistente
for (const peer of this.state.getWebSockets()) peer.send(JSON.stringify(loc));
}
}
# wrangler.toml
name = "logistica-edge"
main = "src/worker.ts"
compatibility_date = "2026-01-15"
compatibility_flags = ["nodejs_compat"]
[[hyperdrive]]
binding = "DB"
id = "abc123hyperdrive_id"
[[durable_objects.bindings]]
name = "COURIER_LOCATION"
class_name = "CourierLocation"
[[migrations]]
tag = "v1"
new_sqlite_classes = ["CourierLocation"]
[ai]
binding = "AI"
Vercel Edge Functions:
Runtime baseado em V8 isolates (similar a Workers mas roda em PoP Vercel). Subset Web Standards, sem Node APIs por default (use runtime: "nodejs" se precisar). Limites: 25s wallclock + 15s CPU, 4MB script, 128MB RAM. Edge Config (KV global ~100ms read), Edge Middleware (executa antes de qualquer route, pra auth/AB/geo).
// app/api/geo/route.ts (Next.js 15+)
export const runtime = "edge"; // V8 isolate
export const preferredRegion = ["gru1", "iad1"]; // São Paulo + Virginia
export async function GET(req: Request) {
const country = req.headers.get("x-vercel-ip-country") ?? "BR";
return Response.json({ country, region: process.env.VERCEL_REGION });
}
Deno Deploy:
Web Standards-first (Deno runtime nasceu com isso). 35 regiões globais 2026, V8 isolates, TLA (top-level await) suportado. Deno KV (FoundationDB-backed, strong consistency em região, eventual cross-region). Deno Subhosting pra multi-tenant (rodar código de cliente isoladamente).
// main.ts (Deno Deploy)
Deno.serve(async (req) => {
const kv = await Deno.openKv();
const { value } = await kv.get<number>(["counter"]);
await kv.set(["counter"], (value ?? 0) + 1);
return new Response(`count: ${value}`);
});
Bun em 2026:
Bun não é edge runtime — é JS runtime alternativo a Node, roda em container/VM tradicional. Bun 1.2 (Q1 2026) entrega Node compat ~95%, bundler nativo, test runner, package manager. Cold start em Lambda Bun: ~80ms (vs Node ~200ms), porque binário único Zig sem JIT warmup pesado. Use Bun pra: APIs Node-compat com cold start menor, build tooling rápido. Não é substituto pra Workers — quando precisa edge global use Workers/Vercel/Deno.
Constraints comparison 2026:
| Runtime | CPU max | Wallclock | RAM | Script size | Node compat | Cold start | Regiões |
|---|---|---|---|---|---|---|---|
| Cloudflare Workers (paid) | 30s | 5min (waitUntil) | 128MB | 10MB compressed | nodejs_compat flag | ~5ms | 330+ |
| Vercel Edge | 15s | 25s | 128MB | 4MB | limitado | ~10ms | 18 PoP |
| Deno Deploy | — | sem limite hard | 512MB | — | parcial via npm: | ~10ms | 35 |
| AWS Lambda Node | 15min | 15min | 10GB | 250MB unzipped | full | 200-1000ms | 30+ regiões (não PoP) |
| AWS Lambda@Edge | 30s | 30s | 10GB | 50MB | full | 100-300ms | CloudFront PoP |
| Bun runtime (container) | host-limited | host-limited | host-limited | host-limited | ~95% | ~80ms (Lambda) | onde host roda |
Durable Objects pattern — quando vale:
DO é single-instance global por ID: 1 réplica fisica para o objeto room:abc123 no planeta inteiro. Strong consistency trivial (sem distributed transaction), latência variável (cliente em SP acessa DO em IAD = 150ms RTT). Casos:
Não use DO pra: armazenar 1 DO por usuário (use D1/Postgres), workload write-heavy single-object (1 DO ≠ infinite throughput, ~100-1000 ops/s por instance), dado que pode viver eventually consistent (use KV).
When edge wins:
When edge perde:
Stack Logística aplicada: courier location updates websocket via Durable Object (1 DO por courier, motoboy + dispatcher conectam mesmo objeto, broadcast lat/lng a cada 2s). Workers fetch handler resolve Hyperdrive → Postgres central pra orders/customers (read-heavy cached em KV 30s TTL). Edge middleware Vercel pra auth check JWT antes de qualquer route Next.js. Workers AI pra classificar incidentes (foto entrega → "danificado/ok") sem chamar OpenAI external. Lambda fora de edge pra batch noturno reconciliação financeira (rodada 30min, 8GB RAM, 10K orders).
10 anti-patterns:
nodejs_compat: import sharp from "sharp" em Workers sem flag → deploy fail (unknown module: node:fs). Habilite flag ou troque dep.await kv.put("balance", v); await kv.get("balance") em outro PoP devolve valor antigo até ~60s. Use DO storage ou D1.postgres(url, { max: 100 }) em 1000 isolates simultâneos → connection storm no Postgres. Use max: 3-5 por isolate; Hyperdrive multiplexa.fetch externo síncrono em handler: bloqueia CPU time. Use c.executionCtx.waitUntil() pra fire-and-forget telemetria.runtime: "edge" e runtime: "nodejs" sem critério em Next.js: deploy fail por imports incompatíveis (Prisma full ORM em edge sem driver adapter).state.acceptWebSocket() (hibernation) → sobrevive a evictions.Cruza com 04-08 §2.4 (serverless geral), 04-08 §2.13 (edge functions intro), 04-08 §2.20 (decisão arquitetura), 03-12 (WebAssembly em edge), 02-14 §2.15 (WebSockets em edge runtime), 03-09 §2.6.1 (edge rendering frontend perf), 04-09 (multi-region scaling).
Você precisa, sem consultar:
Decision document + 1 extraction real no Logística.
ARCHITECTURE-DECISION.md:
/webhooks/* pro Go service; resto pro monolith.order_management.orders, courier.couriers, billing.invoices).external_events próprio, isolado./v1/track/{token} (status público read-only) servindo ETag/cache forte da edge.Threshold de Maestria
Acerte todas as 5 pra marcar o módulo como concluído. Sem pressa, sem timer. Tudo fica salvo no teu navegador.
Q1O que caracteriza o anti-pattern 'distributed monolith'?
Q2Qual é o raciocínio principal por trás de 'monolith first' (Sam Newman)?
Q3Em multi-tenancy pool com Row-Level Security, qual é o demônio operacional principal?
Q4Por que Durable Objects (Cloudflare) não devem ser usados como DB geral (1 DO por entidade)?
Q5No Strangler Fig pattern, qual é a função correta da Façade na fase 1?
Destrava
04-08 é prereq dos seguintes módulos: