Post: Criando uma classe de conexão Firedac no modelo MVC

Alessandro Medeiros

Neste post irei mostrar como eu crio minhas classes de conexões deixando-as de uma forma genérica, onde não ficamos refém de componentes de conexão, onde ficará muito fácil se quisermos trocar do Firedac para uma outra middle de conexão.

Isso garante que nosso projeto tenha um tempo de vida útil muito maior, pois desacoplando nossa classe de conexão ficamos livres para facilmente migrarmos para qualquer componente de acesso a banco.

Você ainda está preso ao passado?

Muitas pessoas estão ainda presas com seus projetos completamente acoplados, e muitos ainda nem conseguiram se livrar do BDE até hoje, ou ainda estão usando o DBExpress e não conseguem mudar para o Firedac, porque existe muita dependência dentro do seu código, colocaram muitos componentes em seus formulários, com isso gerando uma dependência, dificultando que seu projeto venha evoluir, deixando seu software refém dos componentes de acesso, e isso não é legal para o nosso mercado, onde constantemente vem sofrendo mudanças e nossos softwares ficando cada dia mais para trás e não acompanhando esse crescimento.

Organizando a casa.

Iremos trabalhar com MVC e Programação Funcional, onde iremos criar interfaces e classes que implementem essas interfaces, deixando nossos códigos mais limpos e de fácil manutenção.

Iremos abstrair para as interfaces todas as propriedades das conexões com o banco de dados, desta forma quando abstrairmos nós não ficamos refém de componentes de conexão, sendo assim fica muito mais fácil se quisermos trocar de componente de conexão.

Agora vamos implementar nosso model de conexão, neste ponto iremos criar nossas interfaces e as classes que implementam essas interfaces, essas padrões explico melhor em um dos meus treinamentos de MVC com DELPHI.

Nesta classe que implementa a interface de conexão já iremos fazer diretamente para o firedac, mas a forma que iremos implementar já irá ficar pronto para que possa ser usado qualquer outro Middle de acesso.

Primeiro vamos criar nossas interfaces, nesse código abaixo podemos ver que foi criado a interface iModelConexao, essa é a interface padrão que todos os Middle de acesso irão trabalhar em nossos projetos.

 

 


type
    iModelQuery = interface;

    iModelConexao = interface
        function Connection : TObject;
    end;

    iModelConexaoFactory = interface
        function Conexao : iModelConexao;
        function Query : iModelQuery;
    end;

    iModelQuery = interface
        function Query : TObject;
        function Open(aSQL : String) : iModelQuery;
        function ExecSQL(aSQL : String) : iModelQuery;
    end;

    iModelEntidade = interface
        function DataSet ( aValue : TDataSource ) : iModelEntidade;
        procedure Open;
    end;
    iModelEntidadeFactory = interface
        function Produto : iModelEntidade;
    end;

Em nossa interface iModelConexao nós possuímos o método Connection, e neste retorna TObject, onde todo componente do Delphi herdam de TObject.

Quando definimos um retorno mais ancestral em nossos método, podemos retornar qualquer objeto de conexão, deixando assim nossas implementações mais genéricas.

Com nossas interfaces criadas agora vamos criar as classes que implementam essas interfaces.


Type
    TModelFiredacConexao = class(TInterfacedObject, iModelConexao)
    private
        FConexao : TFDConnection;
    public
       constructor Create;
       destructor Destroy; override;
       class function New : iModelConexao;
       function Connection : TObject;
    end;

implementation

{ TModelFiredacConexao }

function TModelFiredacConexao.Connection: TObject;
begin
    Result := FConexao;
end;

constructor TModelFiredacConexao.Create;
begin
    FConexao := TFDConnection.Create(nil);
    FConexao.Params.DriverID := 'FB';
    FConexao.Params.Database := 'localhost/3050:C:\MVC_Blog\Database\DADOS.FDB';
    FConexao.Params.UserName := 'SYSDBA';
    FConexao.Params.Password := 'masterkey';
    FConexao.Connected := true;
end;

destructor TModelFiredacConexao.Destroy;
begin
    FreeAndNil(FConexao);
    inherited;
end;

class function TModelFiredacConexao.New: iModelConexao;
begin
  Result := Self.Create;
end;

Como vamos trabalhar com o Firedac iremos precisar de um objeto de conexão, para isso temos o FConexao do tipo TFDConnection, no método Connection que retorna o objeto concreto de conexão com o retorno desse o objeto FConexao.

Como não criamos componentes em tela precisamos criar nosso objeto, para isso no método create dessa classe é criado esse objeto, e passamos também os atributos de conexão desse objeto, pois quando é solicitado uma conexão já irá ser recebido uma conexão pronta.

iModelQuery

Type
  TModelFiredacQuery = class(TInterfacedObject, iModelQuery)
    private
      FQuery : TFDQuery;
      FConexao : iModelConexao;
    public
      constructor Create(aValue : iModelConexao);
      destructor Destroy; override;
      class function New(aValue : iModelConexao) : iModelQuery;
      function Query : TObject;
      function Open(aSQL : String) : iModelQuery;
      function ExecSQL(aSQL : String) : iModelQuery;
  end;

implementation

{ TModelFiredacQuery }

constructor TModelFiredacQuery.Create(aValue: iModelConexao);
begin
  FConexao := aValue;
  FQuery := TFDQuery.Create(nil);
  FQuery.Connection := TFDConnection(FConexao.Connection);
end;

destructor TModelFiredacQuery.Destroy;
begin
  FreeAndNil(FQuery);
  inherited;
end;

function TModelFiredacQuery.ExecSQL(aSQL: String): iModelQuery;
begin
  Result := Self;
  FQuery.ExecSQL(aSQL);
end;

class function TModelFiredacQuery.New(aValue: iModelConexao): iModelQuery;
begin
  Result := Self.Create(aValue);
end;

function TModelFiredacQuery.Open(aSQL: String): iModelQuery;
begin
  Result := Self;
  FQuery.Open(aSQL);
end;

function TModelFiredacQuery.Query: TObject;
begin
  Result := FQuery;
end;

Nesta classe criamos o query do firedac, para que possamos realizar as conexões com as entidades do banco de dados, nesta classe trabalhamos com injeção de dependências,  caso não esteja familiarizado com isso eu explico melhor no curso de POO EXPERT+PROGRAMAÇÃO FUNCIONAL, pois como estamos trabalhando com o TFDQuery ele irá precisar conhecer a conexão, para isso nós passamos para o método New e Create a interface de conexão e não um objeto de conexão, por este motivo é criado o objeto FConexao para armazenar a conexão, fazendo isso deixamos nosso código desacoplado.

 

 

No método create recebemos a conexão, passamos para o objeto FConexão, criamos nossa FQuery e passamos a conexão para ele, mas você pode ver que fazemos um casting pois o FDQuery só trabalha com o TFDConnection.

iModelConexaoFactory:


Type
    TModelConexaoFactory = class(TInterfacedObject, iModelConexaoFactory)
    private
    public
        constructor Create;
        destructor Destroy; override;
        class function New : iModelConexaoFactory;
        function Conexao : iModelConexao;
        function Query : iModelQuery;
    end;

implementation

{ TModelConexaoFactory }

function TModelConexaoFactory.Conexao: iModelConexao;
begin
    Result := TModelFiredacConexao.New;
end;

constructor TModelConexaoFactory.Create;
begin

end;

destructor TModelConexaoFactory.Destroy;
begin
    inherited;
end;

class function TModelConexaoFactory.New: iModelConexaoFactory;
begin
    Result := Self.Create;
end;

function TModelConexaoFactory.Query: iModelQuery;
begin
    Result := TModelFiredacQuery.New(Self.Conexao);
end;

A classe TModelConexaoFactory é a classe responsável de retornar uma instancia das classes de conexão, fazendo com que o meu acoplamento seja menor ainda, pois se em algum lugar a classe precisar do objeto de conexão, ele não irá precisar saber da classe de qualquer Middle de acesso, no curso de MVC é explicado mais afundo sobre isso.

Contemplando os dados.

Agora vamos tratar de nossas entidades, entidade é o espelho das tabelas do nosso banco de dados, visto que já temos nossa conexão pronta para trabalhar e sem dependência de componentes, totalmente desacoplada.

iModelEntidade


Type
    TModelEntidadeProduto = class(TInterfacedObject, iModelEntidade)
    private
        FQuery : iModelQuery;
    public
        constructor Create;
        destructor Destroy; override;
        class function New : iModelEntidade;
        function DataSet ( aValue : TDataSource ) : iModelEntidade;
        procedure Open;
    end;

implementation

{ TModelEntidadeProduto }

constructor TModelEntidadeProduto.Create;
begin
    FQuery := TModelConexaoFactory.New.Query;
end;

function TModelEntidadeProduto.DataSet(aValue: TDataSource): iModelEntidade;
begin
    Result := Self;
    aValue.DataSet := TDataSet(FQuery.Query);
end;

destructor TModelEntidadeProduto.Destroy;
begin
    inherited;
end;

class function TModelEntidadeProduto.New: iModelEntidade;
begin
    Result := Self.Create;
end;

procedure TModelEntidadeProduto.Open;
begin
    FQuery.Open('SELECT * FROM PRODUTO');
end;

Essa classe representa nossa tabela de PRODUTO do banco de dados, e a mesma implementa a interface de iEntidade, herdando todos os seus metodos, temos o objeto FQuery do tipo iModelQuery, que no Create dessa classe nós instanciamos esse objeto a classe TModelConexaoFactory , onde é pedido a Query dessa factory, lembrando que quem implementa não instancia as classes de conexão e query, mas a factory, deixando nossas implementações desacopladas.

No método DataSet nós fazemos um casting do dataset da query, no metodo Open nós passamos o select para a tabela de PRODUTO.

IModelEntidadeFactory:

type
    TModelEntidadesFactory = class(TInterfacedObject, iModelEntidadeFactory)
    private
        FProduto : iModelEntidade;
    public
        constructor Create;
        destructor Destroy; override;
        class function New : iModelEntidadeFactory;
        function Produto : iModelEntidade;
    end;

implementation

{ TModelEntidadesFactory }

constructor TModelEntidadesFactory.Create;
begin

end;

destructor TModelEntidadesFactory.Destroy;
begin
    inherited;
end;

class function TModelEntidadesFactory.New: iModelEntidadeFactory;
begin
    Result := Self.Create;
end;

function TModelEntidadesFactory.Produto: iModelEntidade;
begin
   if not Assigned(FProduto) then
       FProduto := TModelEntidadeProduto.New;
   Result := FProduto;
end;

Como falei anteriormente que a entidade é o espelho das tabelas do banco de dados, nós criamos uma factory que iremos implementar um método para cada entidade do banco de dados, fazendo assim não termos que criar uma classe para cada entidade, deixando tudo por conta de nossa classe factory.

Realizando o controle de tudo.

Assim como no model que está representando a camada que interage com o banco de dados, precisamos de uma outra camada que faz o meio de campo, para isso entra o controller.

Nós criamos a interface de controller que possui o método que tem como retorno a factory das entidades do model.


type
   iController = interface
   function Entidades : iModelEntidadeFactory;
end;

iController


Type
    TController = class(TInterfacedObject, iController)
    private
        FModelEntidades : iModelEntidadeFactory;
    public
        constructor Create;
        destructor Destroy; override;
        class function New : iController;
        function Entidades : iModelEntidadeFactory;
    end;

implementation

{ TController }

constructor TController.Create;
begin
    FModelEntidades := TModelEntidadesFactory.New;
end;

destructor TController.Destroy;
begin
    inherited;
end;

function TController.Entidades: iModelEntidadeFactory;
begin
    Result := FModelEntidades;
end;

class function TController.New: iController;
begin
    Result := Self.Create;
end;

Nesta classe que implementa a interface iController, o nosso controller não precisa conhecer as entidades, e sim a factory, o acoplamento é zero.

A visão além do alcance…

Nós como programadores Delphi estamos muito acostumados a colocar tudo na tela onde nossa camada de visão está ao alcance de tudo, e sendo assim refém dos componentes.

Agora irei lhe mostrar como nossa camada de visão irá receber os dados de nosso banco de dados.

Lembra que falei que nosso controller que faz o meio de campo? Então ele irá pegar essas informações e repassar para a nossa camada de visão.

Em nosso formulário temos um button, dbgrid e um datasource:

Em nossa camada de visão criamos um objeto com o tipo iController, e no evendo oncreate do formulário instanciamos a classe controller:


...

private
    { Private declarations }
    FController : iController;

...

procedure TForm3.FormCreate(Sender: TObject);
begin
    FController := TController.New;
    ReportMemoryLeaksOnShutdown := true;
end;

Desta forma o evento onclick do botão fica mais claro e simples de se implementar a chamada aos dados:


procedure TForm3.Button1Click(Sender: TObject);
begin
    FController
       .Entidades
          .Produto
          .DataSet(DataSource1)
       .Open;
end;

Viu como fica simples programar usando a programação funcional, e usando o padrão MVC, com isso nossa camada de visão não precisa saber quem é o seu provedor de dados, bastando simplesmente você alterar sua Middle de conexão, sem precisar alterar uma linha se quer da camada de visão.

A nossa classe de conexão e todo o código usado nesse artigo pode ser baixando no link a baixo:

BAIXAR OS FONTES DO ARTIGO

 

 

Esse artigo foi retirado de uma das aulas do nosso treinamento de MVC COM DELPHI, nesse treinamento você irá aprender de forma prática como implementar a arquitetura MVC do absoluto zero, criar menus dinâmicos, CRUD completo com estrutura MVC e ainda saberá como seu projeto pode ficar completamente independente de componente de conexão, trabalhando simultaneamente com Firedac e Zeos e ainda exemplos reais de aplicação das técnicas para resolver problemas do dia a dia, Como:

* Criando a estrutura MVC do Zero
* Criando menus desacoplados com MVC
* Estruturando a conexão com Banco de Dados
* CRUD com acoplamento Zero
* Possibilidade de trabalhar com múltiplos componente de conexão no mesmo projeto.

Somente para você caro leitor estou disponibilizando este curso com um super desconto CLICANDO AQUI.

 

 

 

Faça sua busca

CATEGORIAS

POSTS RECENTES

E caso você tem interesse de conhecer mais sobre Criando uma classe de conexão Firedac no modelo MVC, acesse o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI
Você não terá só conteúdos relacionados ao Criando uma classe de conexão Firedac no modelo MVC, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.
Gostou?
Compartilhe:

Embarque no foguete com milhares de devs para aprender desenvolvimento, evoluir de forma contínua e se manter relevante no mercado.

Dúvidas
Cadastre-se em nossa lista

Para ter acesso em primeira mão, a tudo que acontece na Academia do Código, basta se cadastrar em nossa lista

Grupo Thulio Bittencourt | Academia do Código

#FaçaPartedaHistória

Copyright © 2024 – Todos os direitos reservados