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:
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.

