Um Durable Object por Organização com Cloudflare e Drizzle ORM: Feedbacks Multi-Tenant na Prática
Inspirado pelo excelente artigo "One Database Per User with Cloudflare Durable Objects and Drizzle ORM" do Boris Tane, este post explora como aplicar um conceito similar no contexto de um sistema de feedback multi-tenant: um Durable Object por organização.
Ao invés de termos um banco de dados monolítico compartilhado entre todas as organizações, vamos isolar os dados por empresa, criando uma arquitetura mais segura, escalável e fácil de manter — especialmente útil quando lidamos com dados sensíveis como feedbacks de usuários.
🧱 A Arquitetura: Um DO por Organização
Cada organização (ou empresa) possui seu próprio DurableObject
. Isso permite:
- Armazenamento isolado de feedbacks por tenant
- Redução de concorrência entre organizações
- Possibilidade de evoluir schemas de forma independente
- Escalabilidade natural, já que cada DO roda isoladamente
Na prática, cada organização tem uma instância própria de um DurableObject
que contém seu banco de dados SQLite, via Drizzle ORM.
🌐 Roteando com Cloudflare Workers
Nosso Worker
identifica a organização com base no subdomínio (ex: empresaA.superciclo.com
, empresaB.superciclo.com
) e roteia a requisição para o DurableObject
correspondente.
// worker.ts
export default {
async fetch(req: Request, env: Env) {
const url = new URL(req.url);
const orgSlug = getSubdomain(url.hostname);
const id = env.FEEDBACK_OBJECT.idFromName(orgSlug);
const stub = env.FEEDBACK_OBJECT.get(id);
return stub.fetch(req);
},
};
function getSubdomain(hostname: string): string {
const parts = hostname.split(".");
return parts.length > 2 ? parts[0] : "default";
}
🧠 A Lógica do Durable Object
Nosso DurableObject
gerencia a criação do banco SQLite localmente com Drizzle ORM, criando o schema de feedbacks por tenant:
// feedbackObject.ts
import { drizzle } from "drizzle-orm/d1";
import { feedbacks } from "./schema";
export class FeedbackObject {
constructor(state: DurableObjectState, env: Env) {
this.state = state;
this.env = env;
}
async fetch(req: Request) {
const db = drizzle(this.env.DB);
const { method } = req;
if (method === "POST") {
const { message, user } = await req.json();
await db.insert(feedbacks).values({ message, user });
return new Response("Feedback recebido!", { status: 201 });
}
if (method === "GET") {
const results = await db.select().from(feedbacks);
return Response.json(results);
}
return new Response("Método não suportado", { status: 405 });
}
}
🗃️ Schema com Drizzle ORM
Criamos o schema de feedbacks de forma simples:
// schema.ts
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
export const feedbacks = sqliteTable("feedbacks", {
id: text("id").primaryKey().default(randomUUID()),
message: text("message").notNull(),
user: text("user"),
});
Cada instância de DO carrega esse schema de forma isolada.
🧪 Testando: Enviando e Listando Feedbacks
Enviar um feedback:
curl -X POST https://empresaA.superciclo.com -H "Content-Type: application/json" -d '{"message": "Adorei a nova interface!", "user": "joao@exemplo.com"}'
Listar feedbacks:
curl https://empresaA.superciclo.com
🚀 Vantagens da Arquitetura por Organização
- Isolamento Total: cada tenant tem sua base, sem risco de vazar dados entre empresas.
- Escalabilidade Horizontal: a carga é distribuída por organização.
- Baixo Acoplamento: evoluir ou debugar uma instância afeta só aquele tenant.
💡 Considerações Finais
Cloudflare Durable Objects + Drizzle ORM oferece uma maneira poderosa e moderna de construir aplicações multi-tenant com isolamento por design.
Esse padrão se encaixa perfeitamente em sistemas onde os dados por organização precisam ser isolados — como um sistema de feedbacks anônimos, onde segurança e separação são essenciais.
Agradecimento especial ao Boris Tane pelo post original que serviu como inspiração para este!
📎 Referência Original
One Database Per User with Cloudflare Durable Objects and Drizzle ORM por Boris Tane
👨💻 Construído com:
- Cloudflare Workers
- Cloudflare Durable Objects
- Drizzle ORM (SQLite)
- Subdomínios para isolamento de tenant