Pular para o conteúdo

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.


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.

Ownership de Componentes no Delphi

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'
end

O 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 .DFM salva e restaura todas as propriedades.
  • Design visual: Você vê o que está construindo em tempo real.

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.


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?

Acoplamento no RAD Tradicional

Acoplamento significa que partes do seu código estão “grudadas” — uma depende diretamente da outra.

Veja este exemplo típico:

unit UFormClientes;
interface
uses
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:

ProblemaConsequência
Form conhece SQLNão pode reutilizar lógica em outro lugar
Form conhece FireDACTrocar para dbExpress exige reescrever tudo
Lógica no eventoImpossível testar sem abrir o formulário
Tudo juntoQualquer mudança pode quebrar algo inesperado

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:

  1. Interfaces: Contratos que definem O QUE algo faz, não COMO faz
  2. Injeção de Dependência (DI): Em vez de criar dependências, você as recebe

Arquitetura Desacoplada com DI

❌ 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 internamente
end;

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)

O Dext Framework traz toda essa arquitetura moderna para o Delphi de forma simples e elegante.

Redução de Código 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.

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;

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 criado
end;
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:

  1. Encontra as dependências (IClienteRepository, ILogger)
  2. Resolve cada uma delas
  3. Passa para o construtor
  4. Retorna a instância pronta

A maior vantagem de código desacoplado é a testabilidade. Veja como testar o TClienteService sem banco de dados:

[TestFixture]
TClienteServiceTest = class
public
[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

O Dext gerencia automaticamente o ciclo de vida dos objetos:

EscopoComportamentoUso Comum
SingletonUma instância para toda a aplicaçãoLogger, Configuração, Cache
ScopedUma instância por requisição HTTPDbContext, UserSession
TransientNova instância toda vez que pedirValidadores, Factories
Services.AddSingleton<ILogger, TConsoleLogger>; // Criado uma vez
Services.AddScoped<IDbContext, TAppDbContext>; // Uma por request
Services.AddTransient<IValidator, TValidator>; // Sempre nova

O Dext não veio para substituir o RAD — veio para complementá-lo.

Use RAD QuandoUse Dext Quando
Protótipos rápidosAplicações enterprise
CRUDs simplesAPIs REST e microserviços
Ferramentas internasCódigo que precisa de testes
Uma pessoa no projetoEquipes 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.



Dext Framework — Performance nativa, produtividade moderna.