Delphi Moderno com Dext: Do RAD ao Desacoplamento
“Simplicity is Complicated.” — Rob Pike
Se você programa em Delphi há anos, provavelmente já ouviu termos como “acoplamento”, “injeção de dependência” e “testabilidade”. Mas o que isso realmente significa na prática? E por que deveríamos nos importar se nosso código “sempre funcionou”?
Este artigo é para você — o desenvolvedor Delphi que quer entender esses conceitos sem jargões complicados, e descobrir como o Dext Framework pode transformar a forma como você escreve código.
A Beleza do RAD: O Que o Delphi Faz Por Você
Seção intitulada “A Beleza do RAD: O Que o Delphi Faz Por Você”Antes de falar sobre “problemas”, vamos reconhecer o que o Delphi faz muito bem. O RAD (Rapid Application Development) é uma das maiores forças da plataforma.

O Conceito de Ownership
Seção intitulada “O Conceito de Ownership”Quando você arrasta um TButton para um TForm, algo mágico acontece por trás das cortinas:
// O Delphi gera isso automaticamente no .DFM:object Button1: TButton Left = 100 Top = 50 Caption = 'Clique'endO TForm se torna o Owner (dono) do TButton. Isso significa:
- ✅ Gerenciamento de memória automático: Quando o Form é destruído, todos os componentes filhos são destruídos junto.
- ✅ Serialização de estado: O arquivo
.DFMsalva e restaura todas as propriedades. - ✅ Design visual: Você vê o que está construindo em tempo real.
Por Que Isso Funciona Bem
Seção intitulada “Por Que Isso Funciona Bem”Para aplicações simples e médias, esse modelo é perfeito:
procedure TForm1.ButtonSalvarClick(Sender: TObject);begin FDQuery1.SQL.Text := 'INSERT INTO Clientes (Nome) VALUES (:Nome)'; FDQuery1.ParamByName('Nome').AsString := EditNome.Text; FDQuery1.ExecSQL; ShowMessage('Salvo com sucesso!');end;Funciona. É rápido de escrever. O cliente recebe o software funcionando em dias, não meses.
O Problema: Quando o RAD Vira Uma Armadilha
Seção intitulada “O Problema: Quando o RAD Vira Uma Armadilha”Mas o que acontece quando seu sistema cresce? Quando você tem 50 formulários, 200 queries e precisa:
- Adicionar um novo banco de dados?
- Escrever testes automatizados?
- Trocar o componente de envio de e-mail?
- Fazer o código funcionar em mobile?

O Que É Acoplamento (Explicação Simples)
Seção intitulada “O Que É Acoplamento (Explicação Simples)”Acoplamento significa que partes do seu código estão “grudadas” — uma depende diretamente da outra.
Veja este exemplo típico:
unit UFormClientes;
interfaceuses Vcl.Forms, FireDAC.Comp.Client, Data.DB;
type TFormClientes = class(TForm) FDQuery1: TFDQuery; // <-- Dependência direta FDConnection1: TFDConnection; // <-- Dependência direta procedure ButtonSalvarClick(Sender: TObject); end;
implementation
procedure TFormClientes.ButtonSalvarClick(Sender: TObject);begin // O Form sabe EXATAMENTE como salvar no banco FDQuery1.Connection := FDConnection1; FDQuery1.SQL.Text := 'INSERT INTO Clientes...'; FDQuery1.ExecSQL;end;Problemas desse código:
| Problema | Consequência |
|---|---|
| Form conhece SQL | Não pode reutilizar lógica em outro lugar |
| Form conhece FireDAC | Trocar para dbExpress exige reescrever tudo |
| Lógica no evento | Impossível testar sem abrir o formulário |
| Tudo junto | Qualquer mudança pode quebrar algo inesperado |
A Metáfora do Chiclete
Seção intitulada “A Metáfora do Chiclete”Imagine código acoplado como chiclete mascado: tudo gruda em tudo. Quando você tenta puxar uma parte, as outras vêm junto.
Código desacoplado é como peças de LEGO: cada peça tem uma função clara, encaixa em um lugar específico, e pode ser trocada sem destruir a estrutura.
A Solução: Interfaces e Injeção de Dependência
Seção intitulada “A Solução: Interfaces e Injeção de Dependência”A solução para acoplamento tem dois pilares:
- Interfaces: Contratos que definem O QUE algo faz, não COMO faz
- Injeção de Dependência (DI): Em vez de criar dependências, você as recebe

Exemplo Prático: Antes e Depois
Seção intitulada “Exemplo Prático: Antes e Depois”❌ ANTES (Acoplado):
procedure TFormClientes.Salvar;var Query: TFDQuery;begin Query := TFDQuery.Create(nil); // <-- Cria diretamente try Query.Connection := FDConnection1; Query.SQL.Text := 'INSERT INTO Clientes...'; Query.ExecSQL; finally Query.Free; end;end;✅ DEPOIS (Desacoplado):
type IClienteRepository = interface ['{GUID}'] procedure Salvar(const Cliente: TCliente); end;
procedure TClienteService.Salvar(const Cliente: TCliente);begin FRepository.Salvar(Cliente); // <-- Não sabe como funciona internamenteend;A diferença fundamental:
- Antes: O código decide COMO fazer (cria query, conecta, executa)
- Depois: O código apenas pede para alguém fazer (
FRepository)
Dext na Prática: Código Real
Seção intitulada “Dext na Prática: Código Real”O Dext Framework traz toda essa arquitetura moderna para o Delphi de forma simples e elegante.

Hello World com Dext
Seção intitulada “Hello World com Dext”program HelloDext;
{$APPTYPE CONSOLE}
uses Dext.Web;
begin TDextWebHost.CreateDefaultBuilder .Configure(procedure(App: IApplicationBuilder) begin App.MapGet('/hello', procedure(Ctx: IHttpContext) begin Ctx.Response.Write('Hello, Dext!'); end); end) .Build .Run;end.É só isso. Em 15 linhas você tem um servidor HTTP funcionando.
Registrando Serviços com DI
Seção intitulada “Registrando Serviços com DI”A mágica do Dext está no container de DI:
TDextWebHost.CreateDefaultBuilder .ConfigureServices(procedure(Services: IServiceCollection) begin // Interface → Implementação Services.AddScoped<IClienteRepository, TClienteRepository>; Services.AddScoped<IClienteService, TClienteService>; Services.AddSingleton<ILogger, TConsoleLogger>; end) .Configure(procedure(App: IApplicationBuilder) begin App.MapPost('/clientes', procedure(Ctx: IHttpContext) var Service: IClienteService; begin // O Dext injeta automaticamente o serviço correto Service := Ctx.Services.GetRequiredService<IClienteService>; Service.Criar(Ctx.Request.Body.FromJson<TCliente>); Ctx.Response.StatusCode := 201; end); end) .Build .Run;Injeção Automática no Construtor
Seção intitulada “Injeção Automática no Construtor”A forma mais elegante é usar injeção por construtor:
type TClienteService = class(TInterfacedObject, IClienteService) private FRepository: IClienteRepository; FLogger: ILogger; public constructor Create(Repository: IClienteRepository; Logger: ILogger); procedure Criar(const Cliente: TCliente); end;
constructor TClienteService.Create(Repository: IClienteRepository; Logger: ILogger);begin FRepository := Repository; // Recebido, não criado FLogger := Logger; // Recebido, não criadoend;
procedure TClienteService.Criar(const Cliente: TCliente);begin FLogger.Info('Criando cliente: ' + Cliente.Nome); FRepository.Salvar(Cliente);end;Quando você registra TClienteService no container, o Dext automaticamente:
- Encontra as dependências (
IClienteRepository,ILogger) - Resolve cada uma delas
- Passa para o construtor
- Retorna a instância pronta
Testes Unitários: A Prova de Fogo
Seção intitulada “Testes Unitários: A Prova de Fogo”A maior vantagem de código desacoplado é a testabilidade. Veja como testar o TClienteService sem banco de dados:
[TestFixture]TClienteServiceTest = classpublic [Test] procedure Criar_DeveLogarEChamarRepositorio;end;
procedure TClienteServiceTest.Criar_DeveLogarEChamarRepositorio;var MockRepo: Mock<IClienteRepository>; MockLogger: Mock<ILogger>; Service: IClienteService; Cliente: TCliente;begin // Arrange: Cria mocks MockRepo := Mock<IClienteRepository>.Create; MockLogger := Mock<ILogger>.Create;
// Cria serviço com dependências falsas Service := TClienteService.Create(MockRepo.Instance, MockLogger.Instance);
Cliente := TCliente.Create; Cliente.Nome := 'João';
// Act: Executa Service.Criar(Cliente);
// Assert: Verifica comportamento MockLogger.Received.Info(Arg.Contains('João')); MockRepo.Received.Salvar(Cliente);end;O que esse teste prova:
- O serviço funciona corretamente
- Ele loga a ação
- Ele chama o repositório
- Tudo isso sem banco de dados, sem servidor, sem rede
Escopos de Vida: Singleton, Scoped, Transient
Seção intitulada “Escopos de Vida: Singleton, Scoped, Transient”O Dext gerencia automaticamente o ciclo de vida dos objetos:
| Escopo | Comportamento | Uso Comum |
|---|---|---|
| Singleton | Uma instância para toda a aplicação | Logger, Configuração, Cache |
| Scoped | Uma instância por requisição HTTP | DbContext, UserSession |
| Transient | Nova instância toda vez que pedir | Validadores, Factories |
Services.AddSingleton<ILogger, TConsoleLogger>; // Criado uma vezServices.AddScoped<IDbContext, TAppDbContext>; // Uma por requestServices.AddTransient<IValidator, TValidator>; // Sempre novaConclusão: O Melhor dos Dois Mundos
Seção intitulada “Conclusão: O Melhor dos Dois Mundos”O Dext não veio para substituir o RAD — veio para complementá-lo.
| Use RAD Quando | Use Dext Quando |
|---|---|
| Protótipos rápidos | Aplicações enterprise |
| CRUDs simples | APIs REST e microserviços |
| Ferramentas internas | Código que precisa de testes |
| Uma pessoa no projeto | Equipes colaborando |
A beleza está em poder escolher a ferramenta certa para cada situação. E quando você precisar de arquitetura sólida, testabilidade e manutenibilidade, o Dext está pronto para ajudar.
Próximos Passos
Seção intitulada “Próximos Passos”- 📖 Documentação Completa do Dext
- 🚀 Exemplo: Hello World Minimal API
- 🏗️ Enterprise Patterns com Dext
- 🔄 Programação Assíncrona
Dext Framework — Performance nativa, produtividade moderna.