MVC No Delphi - Separação de Camadas
MVC No Delphi - Separação de Camadas
MVC No Delphi - Separação de Camadas
www.devmedia.com.br
[versão para impressão]
Link original: https://www.devmedia.com.br/mvc-no-delphi-separacao-de-camadas/30143
Por que eu devo ler este artigo:O artigo apresenta a teoria do padrão MVC, que é largamente utilizado para
separar a camada lógica da camada de apresentação.
Através de um exemplo que tem por finalidade registrar produtos, clientes e itens vendidos, o MVC
é posto em prática e até mesmo a camada de persistência é desenvolvida, sendo empregado o
framework Aurelius para a tarefa de salvar os dados em um banco de dados relacional.
Para essa incompatibilidade damos o nome de impedância e para resolver este problema foram
concebidos os frameworks ORM (Object Relational Mapping – Mapeamento Objeto Relacional),
estes frameworks desempenham o papel de trazer as classes do sistema para o mesmo nível dos
bancos de dados relacional.
O ORM é utilizado para fazer a abstração do banco de dados e por meio dele é possível fazer
consultas ou persistências no banco sem que seja necessário, por parte do desenvolvedor,
escrever códigos SQL, pois estes são feitos pelo próprio framework.
Outra vantagem de utilizar ORM na hora de desenvolver é a liberdade do programador para utilizar
qualquer banco de dados, ou até mesmo qualquer componente de conexão.
https://www.devmedia.com.br/view/print.php?idcomp=30143 1/15
30/11/2019 MVC no Delphi: separação de camadas
No caso específico do Aurelius, por meio dos adapters (será explicado mais adiante) a aplicação
em si fica completamente isenta de particularidades com o sistema de armazenamento de dados,
ou seja, se futuramente for necessário modificar o BD, basta mudar a classe do Adapter e deixar
que o framework faça a adaptação do outro sistema de armazenamento de dados.
Neste artigo vamos estudar com mais detalhes a persistência de objetos utilizando o framework
Aurelius, que é uma ferramenta brasileira, inicialmente criada como um trabalho de conclusão de
curso e mais tarde, com melhorias, foi incorporado a gama de produtos da TMS.
O Aurelius faz a conexão no BD por meio do padrão Adapter, este que, por sua vez, consiste em
criar uma camada intermediária que expressa componente que, por algum motivo, pode estar fora
da arquitetura de sua aplicação, ou seja, se futuramente ocorrer alguma mudança na classe
adaptada, basta executar as devidas alterações na classe Adapter e o sistema já está pronto para
funcionar normalmente.
Para desempenhar esse trabalho, o Aurelius disponibiliza recursos como, por exemplo, o
Automapping, onde é feito o mapeamento de forma automática, bastando seguir os padrões de
nomenclaturas de classes e atributos como, por exemplo, TClass e FField. Existe também o
mapeamento por Custom Attributes, onde são colocadas tags do nome de cada campo, bem como
o da tabela e assim por diante, conforme as necessidades do sistema.
Uma vez com a conexão feita e as classes mapeadas, é hora de iniciar o desenvolvimento
utilizando o padrão MVC, empregando as camadas model, view e controller. Para a view, que é a
camada de apresentação do sistema, uma boa prática é fazer o protótipo da tela. Na seção Links
está disponível o download da instalação de software que auxilia nessa importante tarefa.
Nessa camada não pode conter nenhuma regra de negócio ou de banco de dados, salvo quando é
utilizado o componente para fazer o databinding. Mais adiante abordaremos esse conceito com
mais detalhes.
Nessa arquitetura temos também a camada Model, que é responsável pela comunicação com o
banco de dados e concentração da regra de negócio, ou seja, nela estão contidos todos os
métodos que utilizam acesso ao banco de dados.
Por fim, a camada Controller é a que controla as ações entre as camadas Model e View. Por ela
passa todo o fluxo de dados para fazer a transição entre essas duas camadas. O Controller
https://www.devmedia.com.br/view/print.php?idcomp=30143 2/15
30/11/2019 MVC no Delphi: separação de camadas
Documentação
Quando se inicia um projeto sem antes ter um planejamento e uma documentação adequada, é
muito comum o retrabalho durante as etapas de desenvolvimento, acarretando o atraso de entrega
do projeto. Portanto, é preferível gastar no início um tempo pensando sobre o comportamento do
sistema diante das regras de negócio, fazendo o protótipo de todas as telas do sistema.
Para este artigo vamos desenvolver uma aplicação utilizando o Delphi XE2 e o framework Aurelius
na sua versão 2.0. Como documentação vamos desenvolver um diagrama de classes para nos
auxiliar no mapeamento das mesmas e fazer o protótipo das telas, para utilizarmos como apoio no
desenvolvimento do sistema e expressar todas as regras de negócio antes do desenvolvimento
propriamente dito.
Diagrama de classes
Teremos também as classes TProduto e TCliente que fazem parte das classes TVenda e
TItenVenda, respectivamente. Nessas classes vamos poder observar como é feita uma
persistência simples com o Aurelius, sem relacionamentos nem listas Já nas classes TVenda e
TItemVenda será possível exemplificar como é a persistência de dados quando estamos utilizando
uma lista ou um relacionamento nas classes de domínio.
https://www.devmedia.com.br/view/print.php?idcomp=30143 3/15
30/11/2019 MVC no Delphi: separação de camadas
No diagrama temos as classes com os devidos relacionamentos, que são muito importantes para
que não cometamos erros na hora de fazer o mapeamento das classes. No diagrama podemos
observar a classe TVenda se relacionando com as demais classes. Temos um relacionamento de
agregação (ver BOX 1) entre as classes TVenda e TCliente e TItemVenda e TProduto. Também
podemos observar um relacionamento de composição (ver BOX 2) entre a classe TVenda e
TItemVenda.
BOX 1. Agregação
Podemos definir uma agregação quando em uma relação Parte-Todo perdermos o Todo e ainda
faz sentido manter a parte no banco de dados, vejamos um exemplo na relação entre as tabelas
TCliente e TVenda, conforme a Figura a.
https://www.devmedia.com.br/view/print.php?idcomp=30143 4/15
30/11/2019 MVC no Delphi: separação de camadas
Os clientes são partes integrantes em uma venda, porém não dependem exclusivamente de uma
venda para existirem no banco de dados, ou seja, faz sentido manter um cliente no banco de
dados sem que este participe de uma venda, pois pode ser que este cliente desempenhe outros
papéis dentro do sistema, quando temos esse tipo de relação, é dado o nome de Agregação. Para
representar a agregação utilizamos a seta “vazia” (em branco).
BOX 2. Composição
É uma relação mais forte, ou seja, neste caso em uma relação Parte-Todo, não faz sentido manter a parte no
banco de dados quando não tem mais a todo, ou seja, a parte depende exclusivamente da existência do todo
para ela também existir, vejamos um exemplo de composição entre as classes TVenda e TItemVenda,
conforme a Figura b.
Com base no diagrama de classes podemos criar as telas de nossa aplicação, lembrando que as
telas criadas como protótipo servem apenas para nortear o desenvolvimento, podendo haver
alterações na aplicação real.
https://www.devmedia.com.br/view/print.php?idcomp=30143 5/15
30/11/2019 MVC no Delphi: separação de camadas
Já a Figura 5 apresenta a tela de venda, com maior complexidade porque nela teremos todas as
classes sendo envolvidas.
https://www.devmedia.com.br/view/print.php?idcomp=30143 6/15
30/11/2019 MVC no Delphi: separação de camadas
Agora que toda a documentação necessária para o desenvolvimento está feita, estamos prontos
para iniciarmos o desenvolvimento do sistema propriamente dito. Não vamos exemplificar aqui
como é o código do mapeamento e conexão com o banco de dados, posto que isso já foi mostrado
em um outro artigo (ver seção Links).
Agora é hora de observar na prática como os “porteiros” da arquitetura MVC funcionam. Nos
controladores estão contidos métodos que fazem a interface entre o modelo e a camada de
apresentação como, por exemplo, as funções que fazem o binding dos dados da view para o
model, e vice-versa.
O binding consiste em simplesmente fazer a transição de dados entre as camadas e pode ser feito
com o componente que o Aurelius nos disponibiliza, o AureliusDataSource. No entanto, para este
artigo vamos fazer o binding sem utilizar este componente, dessa forma, os conceitos serão
melhores fixados.
A Listagem 1 apresenta a função BindInModel, que consiste em obter os dados da view e passar
para o modelo. BindInModel é chamada no controlador do cadastro de clientes, depois de
preencher todos os campos. Ela retornará um objeto com as informações, pronto para ser utilizado
na função para persistir no banco de dados.
https://www.devmedia.com.br/view/print.php?idcomp=30143 7/15
30/11/2019 MVC no Delphi: separação de camadas
Observe que na linha 03 temos a declaração da variável TCliente, ou seja, do domínio. Na linha 06
a classe TCliente é instanciada na variável cliente. Já entre as linhas 07 e 09 são feitas as
transições, ou seja, a obtenção das informações digitadas nas caixas de texto e passadas para o
objeto cliente e por fim, na linha 10 é dado o retorno na função, passando o objeto que recebeu as
informações da view.
Agora que temos um objeto com todas as informações carregadas da tela, podemos persistir no
banco este objeto, utilizando a função Salvar, como mostra a Listagem 2.
Como é uma função booleana, tanto a SaveOrUpdate do modelo como a Salvar do controlador, se
a transação ocorreu com sucess,o é atribuído True ao retorno da função, possibilitando a exibição
de uma mensagem, mostrando sucesso ou falha na gravação dos dados. A Listagem 3 mostra
como a View utiliza o Controller.
https://www.devmedia.com.br/view/print.php?idcomp=30143 8/15
30/11/2019 MVC no Delphi: separação de camadas
02 var
03 cliente : TCliente;
04 begin
05 clienteController := TClienteController.Create;
06 cliente := clienteController.BindInModel;
07 clienteController.Salvar(cliente);
08 end;
Na linha 03 podemos observar a declaração da variável do tipo TCliente que receberá da função
BindInModel os dados digitados pelo usuário na tela, como está sendo exemplificado na linha 06.
Na linha 07 temos a chamada da função Salvar, onde passamos o objeto cliente que será
persistido no banco de dados.
Observe que na linha 04 da Listagem 4 temos o atributo FObjectManager que foi criado na hora
de fazer a conexão com o banco de dados, onde temos um RegisterMapping. Este recebe todas as
classes de domínio que as registra na conexão, e o ObjectManager tem a função de gerenciar o
objeto.
É por meio dele que persistimos ou recuperamos dados do BD, passando o objeto como parâmetro
e chamando a função SaveOrUpdate. Com isso temos a persistência de um objeto simples
utilizando o Aurelius.
O cadastro de produtos, assim como o cadastro de clientes, é simples e não envolve nenhuma
relação entre tabelas, portanto vamos apenas conferir como ficarão as funções e suas chamadas,
assim como no cadastro de clientes. A Listagem 5 mostra a função BindInModel que mantém sua
função principal de transpor as informações da View para o Model.
https://www.devmedia.com.br/view/print.php?idcomp=30143 9/15
30/11/2019 MVC no Delphi: separação de camadas
A Listagem 6 apresenta a função Salvar, bem semelhante ao que foi feito para um cliente.
Neste momento chegamos a um exemplo mais complexo, onde teremos uma persistência que
envolve relacionamentos de agregação e composição entre tabelas, além de um exemplo
utilizando uma lista.
https://www.devmedia.com.br/view/print.php?idcomp=30143 10/15
30/11/2019 MVC no Delphi: separação de camadas
Poderemos também conferir como funciona a busca no banco de dados, por meio das funções que
carregam tanto a combo de clientes como a combo de produtos.
Nessa tela temos um grid em memória para armazenar todos os itens da compra, além das listas
que a partir delas serão carregadas as combos e também os itens da venda.
Assim como nas outras telas, aqui teremos os métodos BindInView, Salvar do controller e Salvar
do model, além das chamadas a partir da view, porém, temos algumas diferenças. No método que
faz o bind no modelo teremos que passar a lista de itens para que estes possam ser inseridos na
venda, e também, temos que passar como parâmetro o objeto cliente para marcar qual foi o cliente
que efetuou a compra.
01 function TVendaController.BindInModel(ListaItens :
02 TObjectList<TItemVenda>; cliente : TCliente) : TVenda;
03 var
04 venda : TVenda;
05 begin
06 venda := TVenda.Create;
07 venda.Data := Now;
08 venda.Items := ListaItens;
09 venda.Cliente := cliente;
10 venda.DataPagamento := Now;
11 venda.Valor := StrToFloat(VendaF.edtQtd.Text);
12 Result := venda
13 end;
Observe que ela é bem parecida com as outras listagens que fazem o BindInModel, no entanto,
nesta temos os dois parâmetros: uma lista que contém todos os itens da venda e é do tipo
TItemVenda, e um objeto que guarda as informações do cliente que efetuou a compra.
Estes parâmetros são passados pela view, pois é nela onde serão definidos o cliente e os itens da
venda. Na linha 06 podemos observar o objeto venda, que é do tipo TVenda, ser instanciado. Já
entre as linhas 07 e 11 temos a atribuição de cada valor em seu devido campo, com destaque para
linha 08.
Nela é feita a atribuição dos itens da venda para a venda, note que basta passar a lista carregada
com os itens da venda para o determinado atributo do objeto venda. Agora atente para a linha 09,
onde atribuímos o objeto cliente na venda atual.
Note que, uma vez a classe mapeada, basta ir atribuindo os objetos com o devido tipo para cada
tributo.
https://www.devmedia.com.br/view/print.php?idcomp=30143 11/15
30/11/2019 MVC no Delphi: separação de camadas
Essa é a função que ainda é acionada por meio da view e recebe o objeto que a função
BindInModel retornou. Essa função chama o método SaveOrUpdate no modelo da venda, método
este que faz a persistência deste objeto que é passado como parâmetro.
Agora que já sabemos como são as funções no controlador da venda, vamos ver como é feita a
chamada dessas funções pela view. A Listagem 10 apresenta como a view faz inserção de um
item na venda.
https://www.devmedia.com.br/view/print.php?idcomp=30143 12/15
30/11/2019 MVC no Delphi: separação de camadas
É declarada uma variável do tipo TItemVenda na linha 06, que é instanciada, ou seja, sendo
preparada para receber os dados de cada item da venda. Nas linhas 7 e 8 são atribuídos os
valores para os atributos de TItemVenda e na linha 09 este objeto é adicionado na lista de itens
que mais tarde será atribuída no atributo Itens do objeto venda.
Entre as linhas 10 e 16, são inseridos os dados do item em um dataset em memória, pois temos
uma Grid para alimentar. Ela servirá para o usuário visualizar todos os itens da venda.
Entre as linhas 17 e 19 é feito um laço percorrendo a lista de itens para que seja somado o valor
total da venda naquele momento, e este valor é atribuído ao edtValor na linha 20. A cada item
inserido este valor é recalculado.
Agora, vamos ao botão que tem o papel de finalizar a venda, e também, chama as funções do
controlador da venda (Listagem 11).
Note que neste lugar o código é igual para qualquer caso. Na linha 05 tem uma variável do tipo
TVenda, que foi declara na linha 03, recebe a objeto que a função BindInView do controller retorna
e é utilizada na linha 06 durante a chamada da função Salvar no controller. Vamos conferir agora
como é feita a função Salvar (Listagem 12) no modelo da venda.
Observe que uma vez que o objeto está devidamente carregado, basta executar o comando
SaveOrUpdate do ObjectManager. Dessa forma, esse objeto será persistido no banco de dados.
No entanto, para realizar uma venda, temos que carregar os clientes e os produtos do banco de
dados. Para isso, vamos a view ver como é a chamada dos métodos no controller e logo em
https://www.devmedia.com.br/view/print.php?idcomp=30143 13/15
30/11/2019 MVC no Delphi: separação de camadas
seguida, vamos até o controller e verificar como é feita a comunicação com o model. A Listagem
13 mostra o momento em que as listas de Cliente e Produto são carregadas no evento OnShow.
Observe que as listas serão disparadas para carregar assim que o formulário de venda aparecer
na tela. Entre as linhas 03 e 05 temos as chamadas das funções do controller que fazem a
interface com o model, passando dessa forma para ele o encargo de carregar as listas. Logo em
seguida, nas linhas 06 e 07 temos duas chamadas de funções locais que servem para carregar as
combos, ou seja, as listas carregadas são utilizadas para carregar a combo.
Logo, temos as listas como espelhos das combos, o que facilita nosso trabalho. A Listagem 14
apresenta o método BuscaProdutos do controller que foi utilizado pela view.
Na linha 03 a consulta no banco de dados é feita por meio de um objeto Criteria do Aurelius. Esse
objeto representa uma busca a ser realizada e no exemplo citado queremos todos produtos para
serem exibidos na combo, apenas adicionamos uma ordem. A ordem é representada pelo objeto
TOrder.Asc('DESCRICAO'). Além de ordenação, podemos adicionar um filtro para trazer
informações iguais às passadas por parâmetro, que é definido por Eq e seu uso seria Eq(‘nome da
propriedade’, ‘filtro’).
https://www.devmedia.com.br/view/print.php?idcomp=30143 14/15
30/11/2019 MVC no Delphi: separação de camadas
Claro que cada tipo de filtro tem seus parâmetros, para isso, é válido ler a documentação do
Aurelius. Podemos também fazer a consulta por Id, que será exemplificada na Listagem 16,
utilizando a classe TProduto.
Veja que é muito simples executar a busca por Id utilizando o Aurelius por meio do método Find do
ObjectiManager, passando o tipo que será consultado e o Id do registro, que automaticamente o
Aurelius fará a busca no banco de dados. Por fim, vamos conferir como é a função no model para
buscar a lista de Clientes (Listagem 17).
Observe que o procedimento é exatamente o mesmo de buscar a lista de produtos, porém dessa
vez, os tipos buscados são o TCliente.
Ao aplicar o padrão MVC vemos as funcionalidades do sistema bem separadas, cada uma sem
seu próprio lugar. Isso facilita manutenção e entendimento do código porque podemos encontrar
com facilidade as funcionalidades, que estão organizadas de acordo com seu papel. Até mesmo a
parte de persistência ficou centralizada no framework Aurelius, isolando a aplicação de qualquer
conhecimento de banco de dados. Isso garante a independência em relação a que banco de dados
utilizar.
Links
Manual do Aurelius
http://www.tmssoftware.com.br/aurelius/doc/aurelius_manual.pdf
https://www.devmedia.com.br/view/print.php?idcomp=30143 15/15