Arquitetura

Nesta página

EmDash integra-se profundamente com Astro para fornecer uma experiência CMS completa. Esta página explica as principais decisões arquiteturais e como as peças se encaixam.

Visão geral de alto nível

┌──────────────────────────────────────────────────────────────────┐
│                         Seu site Astro                            │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │                   Integração EmDash                        │  │
│  │                                                            │  │
│  │  ┌──────────────┐   ┌──────────────┐   ┌───────────────┐   │  │
│  │  │   Conteúdo   │   │    Admin     │   │    Plugins    │   │  │
│  │  │    APIs      │   │    Painel    │   │               │   │  │
│  │  └──────────────┘   └──────────────┘   └───────────────┘   │  │
│  │                                                            │  │
│  │  ┌──────────────────────────────────────────────────────┐  │  │
│  │  │                    Camada de dados                   │  │  │
│  │  │   Banco de dados (D1/SQLite)  +  Storage (R2/S3)     │  │  │
│  │  └──────────────────────────────────────────────────────┘  │  │
│  └────────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │                    Framework Astro                         │  │
│  │         Live Collections · Middleware · Sessões            │  │
│  └────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘

EmDash funciona como uma integração Astro. Ele injeta rotas para o painel de administração e APIs REST, fornece um carregador de conteúdo para Live Collections e gerencia migrações de banco de dados e conexões de armazenamento.

Esquema database-first

Ao contrário dos CMS tradicionais que definem o esquema no código, EmDash armazena as definições do esquema no próprio banco de dados. Duas tabelas do sistema rastreiam a estrutura do seu conteúdo:

  • _emdash_collections — Metadados da coleção (slug, rótulo, recursos)
  • _emdash_fields — Definições de campo para cada coleção

Quando você cria uma coleção “products” com campos title e price através da interface de administração, EmDash:

  1. Insere registros em _emdash_collections e _emdash_fields
  2. Executa ALTER TABLE para criar ec_products com as colunas apropriadas

Este design permite:

  • Modificação do esquema em tempo de execução — Criar e editar tipos de conteúdo sem alterações de código ou reconstruções
  • Configuração amigável para não desenvolvedores — Editores de conteúdo podem projetar seu modelo de dados através da interface
  • Colunas SQL reais — Indexação adequada, chaves estrangeiras e otimização de consultas

Tabelas por coleção

Cada coleção obtém sua própria tabela SQLite com prefixo ec_:

-- Criada quando a coleção "posts" é adicionada
CREATE TABLE ec_posts (
  -- Colunas do sistema (sempre presentes)
  id TEXT PRIMARY KEY,
  slug TEXT UNIQUE,
  status TEXT DEFAULT 'draft',  -- draft, published, scheduled
  author_id TEXT,
  created_at TEXT DEFAULT (datetime('now')),
  updated_at TEXT DEFAULT (datetime('now')),
  published_at TEXT,
  deleted_at TEXT,              -- Exclusão suave
  version INTEGER DEFAULT 1,    -- Bloqueio otimista

  -- Colunas de conteúdo (de suas definições de campo)
  title TEXT NOT NULL,
  content JSON,                 -- Portable Text
  excerpt TEXT
);

Por que tabelas por coleção em vez de uma única tabela de conteúdo com JSON?

  • Colunas SQL reais permitem indexação e consultas adequadas
  • Chaves estrangeiras funcionam corretamente
  • Esquema é autodocumentado no banco de dados
  • Sem sobrecarga de análise JSON para acesso a campos
  • Ferramentas de banco de dados podem inspecionar o esquema diretamente

Integração de Live Collections

EmDash usa as Live Collections do Astro 6 para servir conteúdo em tempo de execução. Alterações de conteúdo estão imediatamente disponíveis sem reconstruções estáticas.

emdashLoader() implementa a interface LiveLoader do Astro:

// src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";

export const collections = {
	_emdash: defineLiveCollection({ loader: emdashLoader() }),
};

Consulte conteúdo usando as funções wrapper fornecidas:

import { getEmDashCollection, getEmDashEntry } from "emdash";

// Obter todos os posts publicados
const { entries: posts } = await getEmDashCollection("posts");

// Obter rascunhos
const { entries: drafts } = await getEmDashCollection("posts", {
	status: "draft",
});

// Obter uma única entrada por slug
const { entry: post } = await getEmDashEntry("posts", "my-post-slug");

Injeção de rotas

A integração EmDash usa a API injectRoute do Astro para adicionar rotas de administração e API:

Padrão de caminhoPropósito
/_emdash/admin/[...path]SPA do painel de administração
/_emdash/api/manifestManifesto admin (coleções, plugins)
/_emdash/api/content/[collection]CRUD para entradas de conteúdo
/_emdash/api/media/*Operações de biblioteca de mídia
/_emdash/api/schema/*Gerenciamento de esquema
/_emdash/api/settingsConfigurações do site
/_emdash/api/menus/*Menus de navegação
/_emdash/api/taxonomies/*Categorias, tags, taxonomias personalizadas

As rotas são injetadas do pacote emdash — nada é copiado para o seu projeto.

Camada de dados

EmDash usa Kysely para consultas SQL com segurança de tipo em todos os bancos de dados suportados:

SQLite

Desenvolvimento local com sqlite({ url: "file:./data.db" })

D1

SQL serverless da Cloudflare com d1({ binding: "DB" })

libSQL

SQLite remoto com libsql({ url: "...", authToken: "..." })

A configuração do banco de dados é passada para a integração em astro.config.mjs:

import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { sqlite } from "emdash/db";
import { local } from "emdash/storage";

export default defineConfig({
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data.db" }),
			storage: local({
				directory: "./uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
		}),
	],
});

Abstração de armazenamento

Arquivos de mídia são armazenados separadamente do banco de dados. EmDash suporta:

  • Sistema de arquivos local — Desenvolvimento e implantações simples
  • Cloudflare R2 — Armazenamento de objetos compatível com S3 no edge
  • Compatível com S3 — Qualquer armazenamento de objetos compatível com S3

Os uploads usam URLs assinadas para uploads diretos de cliente para armazenamento, ignorando os limites de tamanho de corpo dos Workers.

Arquitetura de plugins

Plugins estendem EmDash através de um sistema de hooks inspirado no WordPress:

  • Hooks de conteúdocontent:beforeSave, content:afterSave, content:beforeDelete, content:afterDelete
  • Hooks de mídiamedia:beforeUpload, media:afterUpload
  • Armazenamento isolado — Cada plugin obtém acesso KV com namespace
  • Extensões de UI de administração — Widgets de painel, páginas de configurações, editores de campo personalizados

Plugins podem funcionar em dois modos:

  1. Nativo — Acesso completo ao ambiente host (para plugins próprios)
  2. Sandboxed — Executar em isolados V8 com permissões baseadas em capacidade (para plugins de terceiros no Cloudflare)
// astro.config.mjs
import { seoPlugin } from "@emdash-cms/plugin-seo";

emdash({
	plugins: [seoPlugin({ maxTitleLength: 60 })],
});

Fluxo de solicitação

Uma solicitação de conteúdo típica segue este caminho:

  1. Astro recebe a solicitação — Seu componente de página é executado
  2. Consultar conteúdogetEmDashCollection() chama getLiveCollection() do Astro
  3. O carregador executaemdashLoader consulta a tabela ec_* apropriada via Kysely
  4. Dados retornados — Entradas são mapeadas para o formato de entrada do Astro com id, slug e data
  5. A página renderiza — Seu componente recebe o conteúdo e renderiza HTML

Para solicitações de administração:

  1. Middleware autentica — Valida token de sessão
  2. Rota da API manipula a solicitação — Operações CRUD via repositórios
  3. Hooks são disparadosbeforeCreate, afterUpdate, etc.
  4. Atualizações do banco de dados — Kysely executa SQL
  5. Resposta retornada — Resposta JSON para a SPA de administração

Módulos virtuais

EmDash gera módulos virtuais no momento da compilação para configurar o runtime:

MóduloPropósito
virtual:emdash/configConfiguração de banco de dados e armazenamento
virtual:emdash/dialectFactory de dialeto de banco de dados
virtual:emdash/plugin-adminsImportações estáticas para UI admin de plugins

Esta abordagem garante que os bundlers possam resolver e fazer tree-shake do código de plugins corretamente.

Próximos passos