Nada melhor do que desenvolver um sistema utilizando uma boa arquitetura de software, não é? Uma das arquiteturas mais utilizadas por empresas e desenvolvedores de software é o MVC (Model-View-Controller), padrão que fornece organização, padronização e facilidade de manutenção do código. Esse artigo aborda os passos básicos para a elaboração de um projeto arquitetado em MVC no Delphi. Confira!
Estrutura do projeto
O objetivo principal deste artigo é mostrar a estrutura de pastas em um projeto, a alocação das units dentro dessas pastas e a forma como elas se comunicam entre as camadas. Portanto, vou considerar que você já tem conhecimento dessa arquitetura, noções básicas de Orientação a Objetos e experiência com Delphi, ok? Mesmo assim, se você quiser conhecer os conceitos do MVC, leia também este artigo.
Vamos lá! O primeiro passo é criar a pasta raiz do projeto, como C:\Aplicativo
, por exemplo. Em seguida, criaremos também mais três pastas dentro dela: Model, View e Controller. Essa será a estrutura de subpastas que armazenará nossas units referentes a cada camada. A partir de então, as units criadas deverão ser salvas de acordo com a sua responsabilidade no projeto:
- As units das classes de modelagem deverão ser salvas dentro da subpasta Model
- Já as units de controle deverão ser salvas dentro da subpasta Controller
- Por fim, os formulários deverão ser salvos dentro da subpasta View
- O arquivo de projeto (DPR ou DPROJ) deverá ser salvo fora dessas subpastas, ou seja, no diretório raiz da aplicação
- Demais arquivos (imagens, arquivos texto, arquivos INI…) opcionalmente podem ser salvos em um diretório próprio, como “Arquivos”
A nomenclatura das units também é importante para facilitar a localização dentro do projeto. Uma boa prática é salvá-las com um prefixo representando o nome da camada seguido do nome de domÃnio. Por exemplo, se houver o domÃnio “Cliente”, poderÃamos nomear as units da seguinte forma: classeCliente
, controleCliente
e frmCadastroClientes
. Este último recebe o prefixo “frm” por se tratar de um formulário na camada View, embora este prefixo também possa ser utilizado como “form”, “f_” ou até mesmo “view”.
Alguns desenvolvedores preferem utilizar nomenclaturas em inglês nas units, nomeando-as como classCliente
e controllerCliente
. Na verdade, o padrão de nomenclatura é relativo de cada desenvolvedor, mas o importante é definir um nome que seja condizente com a camada na qual a unit está alocada.
Ao respeitar essa estrutura de pastas, observe que o Delphi organiza automaticamente a disposição das units dentro de suas respectivas pastas no Project Manager:
Comunicação entre as camadas
A comunicação entre as camadas é realizada por meio da instanciação de objetos das classes. Considerando que temos um domÃnio de negócio no projeto chamado “Cliente”, poderÃamos escrever o bloco de código abaixo para salvar um novo cliente no banco de dados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var // variáveis das camadas utilizadas na rotina objetoCliente: TCliente; objetoControle: TControleCliente; begin // instanciação dos objetos objetoCliente := TCliente.Create; // classe Modelo objetoControle := TControleCliente.Create; // classe Controle try // preenchimento dos dados objetoCliente.Codigo := StrToIntDef(edtCodigo.Text, 0); objetoCliente.Nome := Trim(edtNome.Text); objetoCliente.CPF := edtCPF.Text; // chamada da rotina para gravação objetoControle.Salvar(objetoCliente); finally // liberação dos objetos da memória FreeAndNil(objetoCliente); FreeAndNil(objetoControle); end; end; |
Atente-se que, ao chamar o método Salvar
de objControle
, os dados do objetoCliente
ainda não serão efetivamente gravados no banco de dados. Antes disso, eles passam pela camada Controller, responsável por validar os dados do objeto para evitar que eles sejam transferidos para a camada de acesso a dados (Model) com inconsistências.
Observe que no exemplo acima, um dos atributos da classe “Cliente” é o CPF. Podemos escrever uma função na camada Controller para validar o CPF e, em caso de inválido, abortar a operação de gravação e retornar uma mensagem ao usuário. Essa é uma grande vantagem da arquitetura MVC: durante essa operação de validação não há nenhum acesso à camada de acesso a dados. Na prática, é como se a camada de acesso a dados (Model) ainda não soubesse que o usuário está incluindo um novo cliente. Ela só irá receber o objeto quando estiver validado e consistente. Interessante, não é?
A camada “Controle”, por sua vez, terá o seguinte código no método Salvar
:
1 2 3 4 5 6 |
procedure TControleCliente.Salvar(const objetoCliente: TCliente); begin // aqui devem ser escritas as funções de validação objetoCliente.Salvar(objetoCliente); end; |
Camada DAO
No exemplo acima, veja que utilizamos o próprio objeto passado por parâmetro para chamar a função Salvar
da camada Model. Aproveitando a oportunidade, vale ressaltar uma observação importante: muitos desenvolvedores preferem estender a camada Model na arquitetura MVC e criar uma camada exclusiva de acesso a dados, chamada DAO (Data Access Object). Eu confesso que sou um desses desenvolvedores, haha.
Ao criar a camada DAO, é possÃvel separar a modelagem de dados (atributos da classe) e os métodos de acesso a dados (Salvar, Alterar, Excluir, etc) em units diferentes. Lógico, neste caso, é necessário criar mais uma subpasta no diretório da aplicação chamada DAO. A introdução dessa camada também irá interferir na camada Controller. Por exemplo, utilizando o código anterior como comparação, a chamada do método Salvar
é alterada para a seguinte forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
procedure TControleCliente.Salvar(const objetoCliente: TCliente); var objetoDAO: TDAOCliente; begin objetoDAO := TDAOCliente.Create; try // aqui devem ser escritas as funções de validação objetoDAO.Salvar(objetoCliente); finally FreeAndNil(objetoDAO); end; end; |
Embora o código fique ligeiramente maior, a compreensão não fica comprometida. A diferença é que, ao invés de chamar a camada Model para persistir os dados, chamamos a camada DAO.
Conclusão
Antes de finalizar o artigo, gostaria de esclarecer uma dúvida comum de desenvolvedores que começam a trabalhar com Orientação a Objetos no Delphi, principalmente desenvolvedores que vieram de outras linguagens, como C++, C# e Java. Essa dúvida é relacionada aos getters e setters, que são métodos de leitura e escrita dos atributos de uma classe.
No Delphi não é necessário utilizar getters e setters. A ferramenta introduz um modo diferente de manipular os atributos: através de propriedades (property
). Em apenas uma linha, é possÃvel declarar a propriedade e suas variáveis de leitura e escrita, como o exemplo abaixo:
1 2 3 4 5 6 7 |
type TCliente = class private FNome: string; public property Nome: string read FNome write FNome; end; |
Ou então, caso necessário, declarar métodos para leitura e escrita:
1 2 3 4 5 6 7 8 9 10 |
type TCliente = class private FNome: string; procedure SetNome(Valor: string); function GetNome: string; public property Nome: string read GetNome write SetNome; end; |
Legal, não é?
O exemplo contido neste artigo (com alguns incrementos) pode ser baixado neste link.
Pessoal, eu vou ficando por aqui e agradeço a vocês pela visita no blog!
Qualquer dúvida ou dificuldade no desenvolvimento de um projeto em MVC, entre em contato!
Abraços!
Primeira vez que vejo ensinamentos de Delphi Orientado a Objetos.
Parabens!
Espero outros artigos sobres.
Obrigado, Frank. Provavelmente haverá mais artigos sobre Orientação a Objetos!
Muita boa sua explicação… Sou desenvolvedor mas nunca usei o modelo MVC e confesso que tenho dificuldades para tal. Mas vou procurar colocar seus ensinamentos em prática…
Se possÃvel faz um cadastro básico em firebird e com a camada de persistência (DAO).
Parabéns por compartilhar seu conhecimento. Que Deus te abençoe.
Olá, Genilson. Assim que possÃvel, vou desenvolver uma aplicação em MVC com Firebird e postar aqui no blog. Obrigado pelo comentário.
otimo trabalho
esta sendo de muita valia, value.
caro amigo,
como ficaria usando datasnap 2010
tem algum exemplo.
obrigado.
Olá, Jorge! Obrigado por deixar o comentário. Ainda não tive a oportunidade de fazer o teste da arquitetura utilizando DataSnap. Assim que surgir a oportunidade, sem dúvidas vou postar um exemplo.
Parabéns pela iniciativa!
Muitos sites recomendam programação orientada a objetos para Delphi, mas ninguém mostra como fazer. Obrigado por nos ensinar como fazer.
Aguardo ansiosamente para aprender contigo como fazer uma pequena aplicação completa em Delphi Firebird, MVC e DAO.
Aliás, você poderia explicar num outro artigo vantagens e desvantagens em usar programação dataware, MVC, MVP e MGM.
Olá, Roberto! Muito obrigado pelo comentário. Em breve pretendo dar continuidade nos artigos sobre implementação de padrões de arquitetura e também de Design Patterns. Provavelmente o MVP também vai entrar na pauta!
Parabéns está bem didático o seu artigo!
Obrigado por deixar o comentário, Leonardo!
Bom dia, André!
Primeiramente queria agradecer pela excelência de seus artigos: tenho aprendido muito aqui e espero aprender ainda mais!
Sobre o MVC, eu tenho lido bastante sobre o assunto e aos poucos vou assimilando os conceitos. Mas uma dificuldade que eu tenho, que pelo jeito é a mesma do colega Genilson Soares, é de que forma exatamente deve ser implementada a persistência dos dados…
Na verdade eu até tenho algumas ideias, como criar um DataModule e colocar os objetos (queries, etc) referentes à s minhas classes lá. Mas fico com a impressão de que isso violaria a ideia básica do MVC, ou de que talvez acabe colocando um pouco de programação estruturada onde não era para te-la… Ou mesmo que, apesar de funcionar, não seja o melhor método! Mas o que seria o ideal? Criar os componentes de acesso aos dados em tempo de execução, por exemplo?
Quero ressaltar que não estou querendo que você entregue tudo de bandeja: pesquisei muita coisa a respeito de MVC e os artigos que encontrei param justamente na parte da persistência, o que me deixou cheio de dúvidas. Como seu exemplo foi de longe o mais didático que achei, se você puder esclarecer esse ponto pra mim e pros outros colegas tenho certeza que ficaria ainda melhor! Ou mesmo seguir a sugestão do Genilson e mostrar um exemplo na prática, isso claro, se seu tempo permitir…
Independente da resposta, obrigado novamente por seu blog! O conhecimento agradece!
Olá Jonathan! Em primeiro lugar, agradeço muito por ter deixado o comentário. Você foi claro, objetivo e explicou muito bem o ponto de vista e a dúvida. Realmente, a camada de persistência, ou simplesmente DAO, é o que confunde os desenvolvedores na arquitetura MVC. Apesar do conceito tradicional do MVC implicar que a DAO está implÃcita dentro da camada Model, muitos preferem desmembrar a modelagem e a persistência em camadas diferentes. Vou lhe enviar um e-mail com mais detalhes. Obrigado!
André, me faltam palavras para dizer o quanto sou grato por tanta prestatividade! Espero que este artigo, bem como os outros e suas dicas sirvam para mostrar pra muita gente que OO no Delphi é muito mais simples do que parece! Obrigado de novo e bom fim de semana!
Olá Celestino, A.L.
Parabenizo-o pela prestatividade (já comentada) e excelência técnica de seus artigos. Estarei acompanhando-o em futuras publicações e gostaria que, se possÃvel, expressasse sua opinião sobre a persistência de dados com o BDOO CACHÉ da Inter System, que suporta uma grande variedade de linguagens e diferentes protocolos.
Olá, Prohulk. Obrigado pela visita e pela sugestão de comentar sobre o BDOO CACHE. Assim que possÃvel, pretendo elaborar um novo artigo sobre o assunto. Abraço!
Obrigado pelo email. Estou estudando o exemplo em Delphi + MVC + DAO que você enviou.
É possÃvel usar DBGrid com MVC respeitando a POO ou devemos usar StringGrid?
Por causa da POO, os componentes dataware tendem a desaparecer?
POO e RAD (dataware) são inimigos mortais?
Olá, Roberto! Fico contente que o exemplo esteja lhe ajudando! Roberto, nada impede que o desenvolvedor utilize uma DBGrid com POO, já que é possÃvel popular um TClientDataSet com dados OleVariant e ligá-lo a uma DBGrid sem problemas. Por exemplo, no MVC, você pode criar uma função que retorne um OleVariant para um TClientDataSet criado na memória (em tempo de execução), e exibir os dados na DBGrid. Em suma, eu não diria que POO e DataWare são inimigos, mas a POO definitivamente reduz a utilização de componentes DataWare e abre espaço para a criação e manipulação de objetos em runtime.
Quando você puder, por favor, faça um artigo ensinando “Por exemplo, no MVC, você pode criar uma função que retorne um OleVariant para um ClientDataSet criado na memória (em tempo de execução), e exibir os dados na DBGrid”.
Isso ajudaria um monte de gente, como eu, que anda perdida ao entrar nesse mundo POO do Delphi…
Por causa dos dbgrids, estou usando dois datasets em cada datamodule: um dataset/datasource para o dbgrid e outro para os objetos da OOP.
O form funciona, mas ainda está confuso de fazer manutenção depois…
Não sei se o fato da empresa ainda usar Delphi 7 também seja mais um complicador ao migrar para POO tudo que antes estava em dataware.
Antes de abandonar o Delphi 7, tenho que jogar fora vários componentes de terceiro que não existem mais para as novas versões do Delphi.
Obrigado novamente pela ajuda e paciência.
Olá, Roberto! Na verdade, a versão 7 do Delphi não dificulta o trabalho com POO, ao menos que você tenha que utilizar tecnologias exclusivas das versões mais recentes. Roberto, em breve vou lhe enviar um exemplo sobre como carregar um TClientDataSet com dados OleVariant. É bem simples! Obrigado novamente pelo comentário!
A coisa fica ainda pior quando me pedem um dbgrid editável…
Olá André, gostei do seu post mas observei que voce faz a validação dos dados (regra de negocio) no proprio controlador nao seria uma boa pratica criar um camada de negocio pra fazer a validações dentro do controlador e usa-lo apenas para direcionar as requisições?
Boa observação, Anderson! Essa questão gera muita controvérsia. No exemplo que desenvolvi, decidi colocar as regras de negócio no Controller, manter o Model exclusivamente para as classes de modelagem e a camada DAO para persistência com o objetivo de demonstrar a separação de responsabilidades. Porém, muitos desenvolvedores empregam uma estrutura mais gerenciada: o Model agrega tanto as classes de modelagem quanto as regras de negócio, enquanto o Controller somente faz o papel de interface entre a View e o Model.
Na empresa em que trabalho, optamos por utilizar o MVP (Model-View-Presenter) nessa estrutura que você mencionou, e é bastante funcional. Toda a regra de negócio é concentrada na camada Model, dispensando o Presenter dessa responsabilidade. Os resultados até o momento foram positivos.
Obrigado pelo comentário!
Ola caro André, muito bom seu arquivo, espero outros posts, você deveria também esta postando sobre banco de dados interbase sera de grande proveito para muitos…grande abraço.
Olá, Getulio! Nos exemplos de blog eu uso bastante o Firebird, que é basicamente uma versão Free do Interbase. Portanto, os exemplos podem ser replicados no Interbase sem problema algum.
Abraço!
Você poderia fazer um artigo demonstrando o MVP (Model-View-Presenter)?
Tem muita diferença entre MVC, MVP, MVvM (Model-View-view-Model) e MGM (Model-GUI-Mediator)?
Olá, Roberto! Sim, há algumas diferenças entre esses padrões de arquitetura, principalmente relacionados às responsabilidades de cada camada, comportamentos dos objetos e a forma como as interações são implementadas. Embora tenham objetivos em comum, os padrões se diferem na forma como são manipulados.
Roberto, o tempo é bastante curto, mas a minha intenção é iniciar uma fase de elaboração de artigos técnicos voltados para Engenharia de Software, como, por exemplo, demonstrar o MVP assim como foi feito com o MVC.
Obrigado pela sugestão!
Quais as principais vantagens e desvantagens entre programar com DAO (Data Access Object) e ORM (Object Relational Mapping)?
Olá, Roberto. Ótima pergunta.
Tanto DAO quanto ORM são camadas de persistência, mas o ORM tira mais proveito da Programação Orientada a Objetos. O objetivo do ORM é evitar que haja um retrabalho ao criar a modelagem das tabelas banco de dados e a modelagem das classes que representam essas tabelas. Em outras palavras, o ORM permite que, por exemplo, a aplicação leia a estrutura de uma tabela e gere uma classe que a represente (em que os campos da tabela são convertidos em atributos da classe).
A camada DAO geralmente não fornece recursos dessa natureza, mas é útil para assumir como intermediária entre a aplicação e o banco de dados, ou seja, receber os dados, validá-los, gerar instruções SQL e garantir a persistências dos dados através de transações.
Olá André.
Achei excelente esta sua demonstração de MVC com Delphi, é difÃcil encontrar algo na web utilizando Delphi, parabéns.
Mas me surgiram algumas dúvidas em um projeto que eu comecei a desenvolver.
Comecei a desenvolver usando o padrão MVC, criando as classes e etc, mas percebi que toda a facilidade que os componentes dataware e o clientdataset são perdidas, principalmente na questão de validações.
Eu vi que eu colocava por exemplo um TEdit e tinha que tratar se for número inteiro, string, real e etc, depois faria todo o processo como você descreveu, mas eu percebi que a camada MODEL poderia ser retirada e deixar isto para o ClientDataSet. Eu fiz na view todos os tipos de validações que eu precisava para os campos dos dataware, no form (a minha view) eu coloco um clientdataset que eu preencho na memória, depois eu gravo o cds em memória com um post e eu passo ele para uma camada de controle(singletone) que é encarregada de fazer as regras de negócio e de chamar as funções para salvar no banco de dados.
O que acha disto? O M do MVC virou o cds, correto? Continua sendo MVC?
Abraços!
Olá, Fábio!
Ao meu ver, você estendeu o padrão MVC e criou uma arquitetura personalizada. Isso é natural, principalmente quando lidamos com regras de negócio e particularidades de um projeto em especÃfico. Bem, se você está gerenciando a modelagem dos seus dados com o TClientDataSet, então podemos afirmar que ele assume a responsabilidade da camada Model. Claro, o TClientDataSet não tem a mesma eficiência que a Orientação a Objetos, já que, com classes, podemos trabalhar com Getters e Setters, heranças, encapsulamento e visibilidade. Porém, utilizar um TClientDataSet para essa finalidade não foge totalmente das diretrizes do MVC.
A propósito, notei que você utiliza Design Patterns no projeto, como o Singleton! Isso é importante!
Obrigado pelo comentário!
Abraço!
Obrigado pela resposta André.
Sim, ainda conheço pouco sobre Design Patterns, mas aos poucos quero me aprofunda, e utilizando Delphi. Eu acho o Delphi uma das melhores ferramentas para se desenvolver software, mas acho uma pena faltar mais material sobre ele, ainda mais relacionado a desenvolvimento OO, Design Patterns e etc.
Abraços.
Concordo com você, Fábio! Infelizmente não é fácil encontrar bons materiais sobre programação orientada a objetos com Delphi. Alguns desenvolvedores normalmente recorrem à materiais em outras linguagens e reproduzem o aprendizado no Delphi. Apesar de um pouco trabalhoso, é uma boa alternativa!
Obrigado novamente pelo comentário! Abraço!
Bom dia.
Preciso de uma ajuda com MVC.
Veja se pode ajudar.
Criei uma Interface cliente.
Contendo todos os campos da tabela cliente.
por exemplo
iCliente
snome
srua
sbairro
slocalidade
snumero
etc…
Faço o sql e jogos todos os dados para
um
Tlist(icliente)
Até aqui tudo ok.
funcionando sem problema.
a Minha duvida é a seguinte
Qd vou montar me grid.
tenho todos os campos no Tlist(iclient).
Só que nao quero mostar todos no grid.
Só que tambem nao quero que seja fixo.
Gostaria de fazer alguma coisa assim como faço hoje usando a query.
for i:=0 to query.fields.count
meugrid.coluna := query.fieldbyname(‘campo’).assgtring;
ou seja so os campos que trago na query e que mostro no grid.
asuando a interface trago todos os campos do banco e jogo na interface..
ai teria que ficar fazendo um if ou deixar fixo.. nao consegui pensar em algum para ser dinamico isso.
pode ajudar.
obrigado.
Olá, Paulo. Infelizmente não consegui compreender muito bem a sua dúvida. Vou entrar em contato para solicitar mais detalhes, ok?
Abraço!
Olá, André parabéns sem sombra de dúvidas excelente seu post e principalmente o funcionamento do seu demo, estava tentando aplicar esse conceito nos meus desenvolvimentos porém sem sucesso. Seu post caiu como uma luva para meu aprendizado. Gostaria se fosse possÃvel que me enviasse mais detalhes da camada de persistência referente ao DAO que você mencionou, pois minha dúvida é igual ao do Jonathan/Genilson. Muito obrigado pela sua publicação está me ajudando e muito. Abraços.
Olá, Alexandre! Muito obrigado pela visita e pelo comentário!
O MVC, por não disponibilizar uma camada exclusiva para a persistência de dados, acaba gerando algumas incertezas mesmo. Porém, nada impede que o desenvolvedor estenda a camada Model e crie uma camada de persistência, no caso, a DAO. Vou lhe enviar um e-mail com mais explicações, ok?
Abraço!
Eu que tenho que agradecer pelo seu post e pelo seu retorno. Pois foi através dele que começou a sair meu primeiro laboratório. E olhe que eu pesquiso muito na net a procura do soluções. Se você tiver algum material a respeito ou até mesmo um curso ficaria muito grato.
Legal, Alexandre! Fico feliz por ter contribuÃdo para o seu primeiro projeto.
Abraço!
Boa noite, tudo bem ? Por um acaso você teria algum exemplo de MVC com Firedac ou onde eu poderia encontrar algum material a respeito ? Obrigado.
Olá, Alexandre! O FireDAC é um recurso relativamente novo, portanto, ainda existem poucos materiais sobre ele. Mesmo assim, a própria documentação do FireDAC no site da Embarcadero já é uma grande fonte de ajuda:
http://docs.embarcadero.com/products/rad_studio/firedac/frames.html
Abraço!
André, muito bom seu artigo sobre MVC parabéns, acredito que um dos mais esclarecidos pra quem trabalha com Delphi Client/Server hoje.
Poderia falar mais sobra a camada DAO e, em quais pontos ela torna mais viável que usar apenas MVC.
Como posso dividir a tarefa entra a Model e a DAO.
Obrigado pela atenção…
Olá, Cleiton, tudo certo?
Já recebi várias dúvidas sobre a camada DAO incorporada no MVC, e algumas delas já foram respondidas nos FAQs do blog.
Vou enviar algumas dessas dúvidas respondidas para o seu e-mail. Espero que elas possam ajudá-lo!
Abraço!
Olá, tudo bem ? precisava tirar uma dúvida com você. Como eu faria para criar um atributo para checar se existe campo auto-incremento ? estou apanhando nessa parte. Obrigado.
Olá, Alexandre!
Não entendi muito bem a sua pergunta. Entrarei em contato, ok?
Abraço!
Olá,
muito boa a explicação, está clareando umas ideias em minha mente. Queria tirar uma duvida, estou desenvolvendo uma aplicação multicamadas ou n-tier para atender clientes desktop e web. Estou usando o Datasnap como servidor REST intercambiando dados via JSON. Estou tentando implementar o máximo possÃvel o padrão de arquitetura MVC, que considero tb n-tier um padrão de arquitetura. No meu caso o Model-View-Controller equivaleria a Camada de Dados – Apresentação e Logica de Negócios? Queri que vc desse uma olhada nessa figura da minha arquitetura https://www.dropbox.com/s/6u5mqybp89ix510/arquiteetura.png?dl=0
Agradeço desde já ajuda.
Olá, Artur!
Rapaz, eu não sei o que houve, mas o seu comentário caiu na minha caixa de spams. Desculpe-me por vê-lo apenas agora.
Vou entrar em contato com você, ok?
Abraço!
Meus Parabéns pela iniciativa, e muito bom adquirir sempre mais conhecimentos e o melhor ainda e compartilhar com todos.
Continue sempre assim.
Olá, Alexandre!
Fico contente que o artigo tenha lhe trazido novos conhecimentos!
Obrigado! Abraço!
Olá, gostei muito do artigo e baixei os arquivos para estudá-los mais amiúde.
Já programo no delphi a algum tempo e confesso que sou amante do Delphi. Comecei a me interessar pelo MVC e por no Delphi. Logo me deparei com o pouco material disponÃvel. Onde estou tendo mais dificuldade é fazer a camada view mandar as informações para o banco de dados, pois ha momentos que nao envia nada ao banco. Em algum ponto estou errando ou deixando escapar algo.
Olá, Renato, tudo certo?
Obrigado pelo comentário! Realmente, Renato, tem muito pouco material sobre implmentação de padrões de arquitetura no Delphi. A partir do ano que vem, pretendo fazer uma série de artigos mais avançados com Delphi, envolvendo padrões de projeto, princÃpios SOLID, etc.
Bom, Renato, em relação à sua pergunta, vale lembrar que a camada View é responsável apenas por apresentar os dados ao usuário. Ela, por si só, não deve enviar dados ao banco. Essa responsabilidade é da Camada Model.
Pensando dessa forma, o procedimento é o seguinte: os dados são digitados pelo usuário na camada View. Em seguida, estes dados são enviados à camada Presenter, que faz o intermédio entre View e Model, realizando as validações necessárias. Por fim, os dados são encaminhados para a camada Model que, por sua vez, persiste os dados no banco. Alguns desenvolvedores criam uma camada adicional, chamada DAO (Data Access Object) exclusivamente para fazer a conexão com o banco de dados e enviar os dados. Neste caso, a camada Model fica responsável apenas por controlar as regras de negócio.
Espero ter ajudado, amigo!
Abraço!
Obrigado pela resposta. Me esclareceu bastante. Mas tenho uma dúvida que é a seguinte: Supondo que no form de pesquisa por exemplo, tenho uma uma grid onde exibiria os dados de uma pesquisa, como trazer os dados a ela tem ter que conectar est form (sendo ele uma view) diretamente no banco? se puder me ajudar nisso, ficaria grato é o que tenho buscado e ainda nao achei de maneira clara como fazer isso.
Olá, Renato!
Vou enviar um e-mail para conversarmos melhor.
Abraço!
Olá, André.
Este artigo melhorou meu entendimento, mas o exemplo foi fundamental. O uso da classe ControleCliente ficou claro pra mim, mas a classe Cliente, não. Ela só declara as variáveis e grava na base. Eessas funções eu faço no form (View), uso o datamobile para armazenar todas tabelas, Querys e Stored Procedures. Parece que estes ficariam na classe Cliente, ou cada classe com as suas.
Vou continuar estudando.
Obrigado.
Olá, Gerson!
A classe TCliente é apenas uma classe de modelagem, ou seja, contém uma estrutura de dados para trabalhar com instâncias na aplicação, facilitando a passagem de parâmetros e a gravação de dados. Geralmente, as propriedades dessa classe correspondem com os campos da tabela no banco de dados.
No conceito do MVC, a camada View (formulário) trata apenas as regras de tela (habilitar/desabilitar campos, foco, mensagens, etc), e não a persistência de dados no banco. Essa função é da camada Model ou DAO.
Boa sorte nos estudos!
Olá,André.
O conceito MVC, classes, polimorfismo, abstração aplica-se ao Delphi e em PHP?
Olá, Gerson!
Sim, todos esses conceitos se aplicam a qualquer linguagem Orientada a Objetos, como Delphi, Java, C#, Visual Basic, PHP, Ruby e Python.
Abraço!
Quero ver um artigo sobre Delphi MVC aplicando as tecnicas de DDD driver domain designer
Olá, Lindermberg, tudo bem?
No momento eu “pausei” a publicação de artigos e só retorno no ano que vem.
Mesmo assim, posso adicionar esse tema na pauta e elaborar um artigo quando possÃvel.
Obrigado pela sugestão. Abraço!
Primeiramente parabéns pelo artigo, como sempre muito didático e funcional.
Sei que o artigo já tem mais de dois anos, mas tenho a mesma dúvida do colega Jonathan Lazaro.
Já trabalho com Delphi a 18 anos, desde a versão 3 e confesso que é muito difÃcil mudar a forma de programar, sair de procedural para OO.
Tenho estudado um pouco sobre esse padrão MVC com DAO e Observer, mas confesso que é complicado, aà cito o colega acima “os artigos que encontrei param justamente na parte da persistência”
Como substituir os datamodules, isso irá de encontro com o padrão MVC???? ainda devo utilizar o datasource??? para onde devo apontá-lo???? São questões que parecem muito “bobas” mas fazem muita diferença pra mim.
Olá, Saulo, tudo bem?
Desculpe-me pela demora para responder.
Bom, vamos lá. Como você disse, migrar do paradigma procedural para OO é realmente um pouco difÃcil, já que envolve uma maneira diferente de modelar a aplicação.
Você pode, sem problemas, continuar utilizando DataModules, assim como DataSets. A maior mudança é como os dados “trafegam” na aplicação. Ao invés de centralizar toda a regra de negócio em uma única tela, como um Cadastro de Clientes, você passa a trabalhar com objetos, seus estados e comportamentos. Pensando de uma forma mais prática, ao cadastrar um novo cliente, terÃamos que instanciar um objeto do tipo “Cliente”, preenchê-lo (com os dados que estão na tela), enviá-lo para uma classe de validação e, por fim, persistir os dados no banco.
A repeito do DataSource, e para onde devemos apontá-lo, continua basicamente a mesma forma. O DataSource continua vinculado a um ClientDataSet, mas, este, por sua vez, ao invés de ser alimentado com um “Open”, deverá basicamente receber os dados de um objeto:
Opa, e sobre a persistência?
Pois bem, Saulo, já vi programadores persistirem os dados com “ApplyUpdates” mesmo, assim como fazemos na programação procedural. Não é errado, porém, também não é o ideal em uma modelagem OO. Eu recomendo que o desenvolvedor crie uma camada de persistência implementando seus próprios métodos de inserção, alteração, exclusão e consultas.
Quando comecei a estudar MVC, conheci um framework chamado DBXExpress (para Firebird) que fornece vários recursos para criar classes de persistência. Assim que possÃvel, gostaria de elaborar alguns artigos sobre ele.
Compreendo que no começo tudo pode parecer um embaralhado de informações, mas é apenas questão de conhecer a desenvolver o conhecimento em OO.
Quando você estiver com muitas dificuldades, talvez eu poderei ajudá-lo: [email protected].
Abraço!
Ótimo! O assunto foi tratado com muita clareza. Parabéns!
Muito obrigado, Rennan! O seu feedback é muito importante.
Abraço!
Muito bom o seu artigo, porém fiquei com uma dúvida referente a aplicação do DAO. Se eu implementar dessa forma separando os dados em uma camada DAO (conforme você descreveu), a rotina “objetoCliente.Salvar(objetoCliente);” dentro do método “TControleCliente.Salvar” será substituÃda por uma rotina dentro do DAO, consequentemente essa rotina “Salvar” no “objetoCliente” não terá mais nenhuma utilidade certo?
A classe TCliente só terá a função de armazenar as propriedades de um objeto Cliente?
Obrigado
Exatamente, Rodrigo!
Se você utilizar a camada DAO, a classe TCliente será exclusivamente de modelagem, somente com atributos e tratamento de dados (como, por exemplo, formatação do CPF e conversão de valores). Em suma, ficará dessa forma:
– Camada View responsável pela apresentação visual;
– Camada Controller responsável pela validação de dados e intermédio entre Model e View;
– Camada Model responsável pela modelagem (e regras de negócio, quando necessário);
– Camada DAO responsável pela persistência e recuperação de dados.
Abraço!
Boa tarde,
Primeiramente parabéns pela iniciativa.
Gostaria de saber se existe algum exemplo utilizando Firebird e a CamadaDAO para melhorar o esquema do exemplo em MVC?
Obrigado e parabéns novamente.
Abs!
Olá, Thiago, tudo bem?
Vou enviar um e-mail pra você com um exemplo que desenvolvi em 2013.
Abraço!
Bom dia, André Celestino. Hoje fico muito grato porque fazia um tempo que estou estudando para começa a programar e sempre quis aprender Delphi. Hoje estou me dedicando mais. Obrigado por você estar nos ajudando com ótimas dicas que nenhum site explica. Irei acompanhar todas a suas dicas. Muito Obrigado, vc é o cara, vlw!
Opa, obrigado pelo feedback, Anderson!
Espero que as dicas, exemplos e orientações do blog possam ajudá-lo no aprendizado em Delphi.
Boa sorte! Abraço!
Boa noite , muito bom
Sobre o retorno dos dados em datasource. Nesta linha ClientDataSet1.Data := objCliente.ConsultarClientes;
Qual o retorno que temos em objCliente.ConsultarClientes; ?
Olá, Everton!
O retorno é do tipo “olevariant”. Este é o tipo de dado que sempre devemos atribuir à propriedade “Data” do DataSet.
Obrigado pela visita.
Abraço!
Oi André…
Parabéns pela facilidade e didática com que explicou o funcionamento do padrão, que é um assunto bem complicado para quem está começando. Eu mesmo fui um dos “abençoados” com a sua forma simples de explicar o modelo.
Porém, como foi uma introdução ao assunto, gostaria que você, se possÃvel, me esclarecesse a forma como é retornada uma consulta da camada DAO para a camada View, para que o assunto fique completo.
Grande abraço.
Olá, Adalberto, como vai?
Fico muito grato ao saber que você gostou do artigo! O meu objetivo é justamente “descomplicar” a Engenharia de Software em programação Delphi.
Bom, mas vamos ao esclarecimento. Alguns DataSets do Delphi, como o TClientDataSet, possui uma propriedade referente ao armazenamento dos dados, chamada “Data”, do tipo OleVariant. Sendo assim, se preenchermos essa propriedade em um DataSet da camada View, os dados já serão exibidos para o usuário, sem mesmo precisar executar um “Open”.
Para que isso aconteça, portanto, é necessário que o método de consulta da camada DAO retorne um dado do tipo OleVariant. Essa é a parte que pode complicar um pouco, já que depende da tecnologia de banco de dados que o desenvolvedor está utilizando. Vamos considerar, por exemplo, que seja Firebird. O método na camada DAO seria basicamente dessa forma:
A camada de controle, por sua vez, teria o mesmo comportamento do código do artigo:
Na View, finalmente, basta chamar o método da classe de controle:
Sei que, a princÃpio, criar um DataSet na camada DAO somente para obter e transportar os dados do tipo OleVariant pode parecer um incômodo, mas, pelo menos neste caso especÃfico, é a maneira mais viável.
Espero que tenha esclarecido a dúvida, Adalberto! Grande abraço!
Opinião.
Delphi é algo que já deveria ter deixado de existir. Ele ainda sobrevive graças a muito código legado e também devido a resistência de alguns programadores em aderir novos conceitos, novas tecnologias e linguagens.
É inegável que para desenvolvimento Windows, .NET/C# ou VB.NET + Visual Studio é o que há de mais moderno e robusto atualmente, é sem dúvida a melhor escolha. Você estará desenvolvendo com uma ferramenta desenvolvida pela própria empresa criadora do SO, isso trás um nÃvel de aproveitamento dos recursos e integração impossÃveis de ser alcançadas com Delphi.
Sem falar no fato de poder desenvolver com paradigmas e padrões modernos que é o caso do MVVM.
Delphi ainda sobrevive graças a muitas empresas que resistem a sair da sua zona de conforto e dedicar algumas horas do fim de semana para aprender, novas tecnologias, paradigmas e filosofia de desenvolvimento.
Me dá calafrios ao ver na empresa onde trabalho, novos projetos sendo desenvolvidos usando algo tão defasado e ainda por cima abrindo mão de paradigmas comprovadamente melhores do ponto de vista da manutenabilidade e reaproveitamento de código que é o OOP.
Não importa o quanto a embarcadero tente, jamais conseguirá entregar algo superior as tecnologias e ferramentas de desenvolvimento Microsoft.
E os motivos são óbvio, embarcadero é uma empresa muito inferior a MS do ponto de vista econômico. MS é desenvolvedora do MS Windows, por isso consegue entregar ferramentas que tiram o máximo de poder do SO e possibilitam uma maior integração com todos os produtos da marca.
Olá, Matheus, tudo bem?
Agradeço por ter expressado a sua opinião no blog.
O Delphi realmente parou no tempo por muitos anos devido à falta de atualizações por parte da Borland. Porém, a Embarcadero, que introduziu a famÃlia XE no Delphi, vem fazendo um ótimo trabalho para trazer a ferramenta de volta à tona. Há muitos recursos novos que são bem proveitosos, conforme demonstrado na Embarcadero Conference todos os anos.
Atualmente, trabalho em um sistema desenvolvido em Delphi que atende Tribunais de Justiça em todo o Brasil, inclusive o TJ de São Paulo, que possui uma imensa massa de dados. O sistema atende plenamente todas as necessidades dos usuários (advogados, representantes e juÃzes) e ainda continua em evolução, ou seja, não é um sistema de código legado. O projeto é tão extenso que foi necessário implantar o SAFe (Scaled Agile Framework) na empresa para administrar as entregas.
Além disso, gostaria também de ressaltar que é possÃvel, sim, alcançar os pilares e paradigmas da Programação Orientada a Objetos com Delphi. Sempre tive a oportunidade de trabalhar com OOP com Delphi, criando interfaces, classes, componentes e bibliotecas sem impedimento algum. Ultimamente, no blog, venho fazendo um trabalho de demonstrar Design Patterns com Delphi, e o retorno da comunidade de programadores (não só de Delphi, mas de .NET e Java também) tem sido muito positivo. Isso ocorre porque a maioria dos bons programadores não estão atrelados à tecnologia, mas aos conceitos de Engenharia de Software.
O desenvolvimento sustentável de software está mais carente de pessoas que conhecem a Engenharia de Software, como Clean Code, SOLID, GRASP, Design Patterns e outras práticas, do que pessoas que conhecem todo o universo de recursos de uma única linguagem de programação. A lógica de programação sempre continuará a mesma. O grande diferencial está na sensibilidade técnica e profissionalismo do desenvolvedor.
Na empresa em que trabalho, há projetos em várias linguagens de programação: C#, Delphi, Java, Python e Ruby, mas não criamos obstáculos e nem barreiras por causa da diferença entre linguagens. Periodicamente, nos reunimos para discutir melhores práticas de programação, e isso está além da tecnologia que usamos.
Eu, particularmente, admiro muito as linguagens da Microsoft. Até mesmo utilizei C# no trabalho de conclusão de curso da pós-graduação e gostei bastante do resultado. No entanto, sempre me identifiquei bastante com Delphi, e toda a minha carreira profissional como programador foi utilizando essa linguagem. Só ressalto que isso, em nenhum momento, me fará abordar um programador Java ou .NET e provar que com Delphi é possÃvel fazer X e Y e que com outras linguagens é impossÃvel. Sempre prezo pela humildade e ética profissional.
De qualquer forma, Matheus, agradeço por ter visitado o blog e desejo boa sorte nos seus projetos!
Grande abraço!
Ola André, venho convidar a todos a conhecer o MVCBr (www.tireideletra.com.br), um framework livre que aplica técnicas de MVC através de assistentes que criam as classes e views… Receber suas observações, serão bem vindas.
Olá, Amarildo!
É um grande prazer receber um comentário seu. Admiro muito o seu trabalho!
Vou divulgar o framework MVCBr. Ainda não tive a oportunidade de utilizá-lo, mas ouvi referências boas referências dele!
Grande abraço!
Olá André, boa tarde!
gosto bastante dos conteúdos que você posta, são de grande ajuda! Vamos à minha dúvida, tenho observado a necessidade de sair da fase “programador de eventos”, tenho uma dúvida, sobre passar a responsabilidade para o banco de dados (através de procedures/functions/trigers) é recomendável? Eu poderia ter ao invés do DAO a utilização direta dos meus objetos CRUD no bd?
Olá, Diego, como vai?
Excelente pergunta! Já recebi questionamentos semelhantes.
Bom, embora não exista uma resposta definitiva, eu vou compartilhar a minha visão, ok?
Programar regras de negócio diretamente no banco de dados é uma boa estratégia para reduzir o código na camada cliente, ou seja, como as regras estarão no banco de dados, não é necessário codificá-las na aplicação.
Porém, eu sempre faço uma ressalva. Em ambientes multi-usuários (com 100, 500, 1000, 5000 usuários acessando o mesmo banco de dados), centralizar as regras de negócio no BD pode não ser uma boa solução, principalmente pelo risco de sobrecarregar o servidor. Imagine, por exemplo, que 1000 usuários façam uma inserção em uma tabela em intervalos de segundos, e essa tabela possui uma stored procedure neste evento. Essas 1000 inserções resultarão em 1000 execuções dessa stored procedure, exigindo que a engine do banco de dados gerencie toda essa demanda. É possÃvel que essa “sobrecarga” reduza a performance do BD, ocasionando pequenos delays na recuperação de dados.
Já quando se trabalha com regras de negócio na camada cliente, o processamento ocorre na estação de cada usuário. Pensando assim, o processamento fica “distribuÃdo” entre as estações, ao invés de ocorrer em apenas um local centralizado.
Pode-se dizer, então, que a escolha pela solução mais adequada depende do segmento do cliente, dimensão do projeto, e volume da massa de dados.
Abraço!
Parabéns pelos artigos! Estou lendo todos e estão sendo muito proveitosos. Poderia me mandar esse exemplo de MVC que mandou pra alguns colegas? Se tiver outro material, será bem-vindo. Obrigado.
Boa noite, Rafael.
Obrigado pelo feedback!
Vou enviar o exemplo no seu e-mail, porém, devo dizer que está um pouco desatualizado (já fazem 4 anos).
Pretendo refazer esse artigo com mais detalhes.
Grande abraço!
Olá. É sem dúvida um excelente artigo. Eu procuro aprender mais a cada dia e confesso que implementar MVC no Delphi tem me dado uma tremenda dor de cabeça. No meu caso especÃfico estou com muita dúvida em relação a visibilidade das units. Eu vou postar um pseudocodigo e gostaria da sua opinião se este é ou não o caminho certo. Vou fazer um exemplo para um Master Detalhe simples
No exemplo acima, considerando que cada Class ou Interface estaria declarada em uma Unit, como ficaria o Uses de cada uma delas?
Você encontrou algum erro conceitual na minha abordagem? O Menu também seria dividio em MVC? Se puder me ajudar, ficarei grato.
Olá, Ricardo, boa tarde.
Peço desculpas pela demora para respondê-lo. Por algum motivo, o seu comentário caiu na categoria de Spams, mas consegui recuperá-lo.
Ricardo, a princÃpio, não vi códigos incorretos na sua abordagem. A camada Controller tem conhecimento da existência das camadas View e Model, atuando como uma intermediária. A seção uses da unit do Controller, portanto, teria as units referentes à s Interfaces da View e Model, já que precisa manipular os dados que trafegam entre elas.
Um dos pontos mais importantes dessa abordagem é a “injeção” das camadas View e Model dentro do Controller, na qual aparentemente você já faz por meio de duas properties. Dessa forma, o Controller fica desacoplado das outras camadas e permite que diferentes combinações sejam possÃveis. Por exemplo, elaborar um “intercâmbio” de camadas de acordo com a funcionalidade acionada pelo usuário.
Ricardo, caso você tenha mais alguma dúvida, envie um e-mail para “[email protected]”.
Grande abraço!
Muito bem explicado! Sempre venho aqui estudar, você realmente tem ótimos artigos. Parabéns André!
Muito obrigado, Ariel! Comentários como o seu me motivam bastante para continuar ester trabalho! 🙂
Continue acompanhando o blog. Em breve volto à ativa!
Grande abraço!
Rapaz, analisando vi que o seu exemplo é muito fraco em POO e não atende no meu ponto de vista um as regras de Padrões de Projetos MVC que eu conheço. Em pascal para se fazer o uso de POO e associar os Padrões MVC é preciso usar interfaces, seu Controller está fazendo a tratamentos que é responsabilidade do Model. Veja o controller tem a responsabilidade apenas de passar para o Model e o Model faz os tratamentos da Regra de Negócio (Validações, Detecção de Erro e Etc), depois devolve para o Controller e o mesmo envia para o View renderizar uma resposta de acordo com o resultado.
Olá, Patrick, tudo bem?
Agradeço o seu comentário, mas discordo de alguns pontos. Pelo que conheço do padrão MVC, a camada Model envolve a modelagem das classes da arquitetura (por isso o nome “Model”), portanto, não deve armazenar validações a nÃvel de visão. A camada Controller, por sua vez, atua como um “Firewall” entre View e Model, evitando que dados inválidos ou inconsistentes trafeguem no fluxo da aplicação.
Bom, mesmo assim, Patrick, assumo que há algumas falhas no artigo. Na época em que foi elaborado (pouco mais de 4 anos), o meu conhecimento em MVC não era tão assertivo quanto atualmente. Por esse motivo, já sinalizei que pretendo reescrevê-lo aplicando os conhecimentos que adquiri desde 2013, fazendo uso de Interfaces, Generics e/ou até mesmo RTTI.
Decidi manter o artigo publicado porque serviu como apoio para vários desenvolvedores, como você pode observar nos comentários anteriores.
Abraço.
Patrick nos disponibilize um exemplo em Delphi de MVC com 100% de POO já que você tem tanto conhecimento assim! Crie um exemplo com Interface, Generics, RTTI, as Validações na Model, etc. Ao invés de perder tempo julgando e criticando o trabalho do outro, perca tempo ajudando as pessoas! Será que você é capaz disso?
Show de bola André, bem objetivo…Poderia me mandar esse exemplo de MVC que mandou pra alguns colegas? Se tiver outro material, será bem-vindo. Obrigado.
Obrigado, Elisangelo!
Estou prestes a elaborar um novo artigo sobre MVC com Delphi, já que este está bastante defasado (escrito há quase 5 anos).
Neste novo artigo, pretendo utilizar recursos mais modernos da linguagem, como Generics e Attributes, por exemplo.
Quando estiver pronto, lhe aviso!
Abraço!
Obrigado André.
Parabéns pelo artigo André!
Só estou com uma pequena dúvida… nas validações que implemento, costumo jogar o foco para o campo que não passou na validação. Você tem alguma sugestão de como fazer isso usando Arquitetura MVC?
Abraços!
Olá, Carlos, tudo bem?
Ótima pergunta! Em breve pretendo refazer este artigo sobre MVC utilizando melhores práticas de programação e abordando alguns detalhes, como essas validações.
De qualquer forma, vamos à resposta: como sabemos, a camada Controller conhece os métodos e os componentes da camada View. Sendo assim, durante uma validação, para focar o componente desejado, basta acessar diretamente o componente da View:
Se você estiver trabalhando com Interfaces, provavelmente terá métodos getters e setters para os componentes da camada View. Neste caso, o código ficaria dessa forma:
E, por sua vez, este seria o getter na View:
Espero ter ajudado! Abraço!
Querido André,
Tenho martelado meus neurônios para mudança de paradigma e encontrei algumas dificuldades no que diz respeito a questões como Mestre-Detalhe no MVC e até mesmo no que o colega Ricardo da Rocha citou acima, que confesso não entendi nada.. rsrs.
Gostaria muito de entender melhor e continuar praticando baseado nos conceitos ensinados por você aqui.
Parabéns e continue no ensinado.
Grande abraço!
Olá, Edicleyto, boa noite!
Não se preocupe! Entrar em um novo paradigma é desafiador mesmo, rsrs.
O MVC é uma abordagem bastante orientada a objetos, diferente do que desenvolvedores Delphi estão acostumados a codificar habitualmente. No caso de Mestre-Detalhe, uma das opções é trabalhar com o conceito de composição, em que um objeto do tipo “Mestre” possui uma lista de objetos do tipo “Detalhe”. Outra opção, mais próxima do que conhecemos atualmente, é consultar mestre e detalhes de forma separada, tratando os seus dados de forma independente também.
Pretendo ainda elaborar um artigo entrando em mais detalhes para “descomplicar” este assunto.
Grande abraço!
Parabéns pelo artigo André. Também tenho interesse no exemplo que você disponibilizou para os colegas acima.
Olá, Elvis, boa noite!
O projeto de exemplo está bastante defasado, já que foi elaborado há 5 anos.
Em breve pretendo reescrever este artigo e codificar o projeto novamente, mas, dessa vez, utilizando alguns recursos importantes do Delphi, como Generics.
Abraço!
Muito bom, fico no aguardo do novo artigo.
Certo!
Muito bom!! Mas acho que a camada View não poderia ter acesso a camada Model. A View somente poderia ter acesso à camada Controller, e por sua vez o Controller manipulava o Model (classes) e também outros Views, assim ficaria mais dividido. É apenas uma opinião.
Olá, Eliomar, boa noite! Concordo com a sua opinião.
Hoje existem várias interpretações do modelo MVC. Em uma delas, a View não deve conhecer a camada de modelagem, como você mencionou. Em outra representação, a View pode criar uma instância da camada Model apenas para transporte de dados, como apresentado no artigo.
O importante, independente da modalidade, é que as responsabilidades fiquem divididas e que não existam fortes acoplamentos entre as camadas.
Abraço!
Oi Andre tudo beleza ?
Parabéns pela matéria, apesar de ser de 2013, ainda é muuuuuuito boa. Pois muitos programadores Delphi ainda hoje persistem no modelo procedural e ai, dá-lhe código espalhado nos formulários. Manutenção disso… aff neim me diga. Eu sou um desses programadores. Estou migrando meus sistemas e gostaria de usar esse modelo ensinado por ti. Porém tenho algumas dúvidas que não consigo entender devido ao modo procedural de resolver os problemas.
Por exemplo :
No Exemplo do CPF somente quando o cliente clicar em gravar é que será avisado do CPF inválido.
Outro exemplo, estou fazendo uma venda e no item preciso saber se o código do produto digitado existe e em seguida descobrir se esse produto possui estoque… dai somente quando o cliente clicar em gravar é que ele será notificado de algo errado?
No modelo procedural nos eventos onExit, é que testo isso… mas da forma MVC não entendi como fazer isso. Ou o correto é criar métodos na classe que faz essas validações e então chamar quando precisar? Ou seja, no evento onexit do código do produto da minha venda eu colocaria algo como:
Olá, donron, boa noite!
Peço desculpas pela demora. Fiquei um pouco ausente devido a alguns problemas de saúde na semana passada.
Rapaz, excelente pergunta! Já recebi dúvidas semelhantes à sua, e o correto é a segunda opção que você mencionou: “criar métodos na classe que faz as validações e chamá-los quando precisar”. Vou explicar o motivo:
O modelo MVC não exige que todas as validações sejam necessariamente ao gravar um registro. Essa decisão, na verdade, é até uma questão de usabilidade. Para o usuário, é muito melhor receber um retorno imediato (por exemplo, CPF inválido ou produto sem estoque), do que um retorno tardio, ou seja, apenas ao gravar o registro. Portanto, é válido executar as validações sob demanda.
A exigência do MVC dividir as responsabilidades em camadas. Seria incorreto, por exemplo, se a validação do CPF fosse codificada na camada View, ao invés de ser codificada na camada Controller.
O código que você postou está correto, e tenho apenas uma observação a fazer: se houverem várias validações no mesmo formulário, talvez seja mais viável declarar o Controller como campo da classe (private), instanciá-lo no evento
OnCreate
do formulário e destruÃ-lo no eventoOnDestroy
. Dessa forma, você terá sempre uma instância do Controller disponÃvel para chamar as validações. Caso contrário, você terá que instanciar e destruir a Controller em cada validação.Espero ter respondido a sua dúvida, donron.
Abraço!
Olá, boa tarde! Parabéns pelo artigo!
Um dúvida me ocorreu: Como ficaria esse conceito se utilizando DATASNAP, tomando como base o seu exemplo?
A VIEW obrigatoriamente ficaria na CLIENT, mas e a CONTROLLER (Métodos de validação, etc,…), MODEL (propriedades de TCliente) e DAO(Conexão com DB e instruções SQL)?
Se MODEL e DAO ficar em SERVER, como “enxergar” a classe e seus métodos de persistência?
Abraço e Deus o abençoe!
Olá, Fábio, tudo bem?
Excelente pergunta! E a resposta é um pouco difÃcil, rsrs.
Fábio, antes de iniciar, vale destacar que o seu sistema, por natureza do DataSnap, já está dividido em camadas: cliente, servidor e banco de dados. Essa divisão já denota a separação de responsabilidades, que é algo bastante vantajoso.
O seu raciocÃnio está correto. Com o MVC, a View ficaria na aplicação Client. A camada Controller, por sua vez, poderia ter duas variações:
A camada Model, no Client, poderia ser um objeto DTO (Data Transfer Object), com o objetivo exclusivo de apenas levar os dados até o servidor, como um transporte. No servidor, a camada Model seria a modelagem das entidades, como
TCliente
.Observe que o modelo MVC se comporta de diferentes maneiras em cada uma das camadas. No Client, não há persistência de dados. No Server, não há formulários visuais. E respondendo à última pergunta: para que o Client “enxergue” os métodos do Server, você pode ter classes compartilhadas, ou seja, arquivos que fazem parte tanto do projeto Client quanto do Server.
Fábio, mesmo assim, vou deixar minha opinião: com DataSnap o projeto já está dividido em camadas. Introduzir o modelo MVC dentro dessa arquitetura pode trazer algumas vantagens, mas, talvez, pode também trazer algumas complicações. No seu caso, é mais fácil “adaptar” o MVC à sua realidade do que fazer o inverso. O importante, no final das contas, é que cada camada tenha apenas uma responsabilidade.
Obrigado pelo comentário!
Abraço!
Grande Professor André, cara se você soubesse o quanto aprendo com suas dicas não é brincadeira, de tantas tentativas tentando aprender um pouco sobre MVC, o seu exemplo, foi o único que compreendi. André não haveria a possibilidade de acrescentar nesse exemplo, o conceito de interface, aplicando os metodos de inserção, edição e exclusão?
Olá, Matheus, boa noite!
Em primeiro lugar, fico imensamente feliz por ter lhe ajudado nessa compreensão do MVC!
Matheus, como você pode ter notado, esse artigo é de 2013, ou seja, foi publicado há 7 anos. De lá pra cá, o Delphi mudou, alguns conceitos evoluÃram, e aprimorei meus estudos em programação. Por conta disso, pretendo “refazer” este artigo abordando conceitos mais avançados, como Interfaces, DI e Design Patterns. Ainda não tenho uma previsão por conta de outros projetos que estou envolvido, mas está na minha pauta!
Grande abraço!
Professor, tem diferença entre chamar a camada DAO pelo constructor e pelo método Salvar? Conforme o exemplo acima, você está chamando a camada DAO, através do método salvar, eu costumo criar a DAO no constructor do meu controlador, se existir alguma explicação pra isso por favor me oriente, pra mim corrigir a minha forma de programar e entender o porque. Deus o abençoe.
Olá, Matheus!
Não há nenhum problema em criar a camada DAO no constructor do Controller. No entanto, acho importante ter o método “Salvar” por 4 motivos:
1) O construtor geralmente é utilizado apenas para instanciar objetos e inicializar variáveis. Não é uma boa prática implementar ações nesse momento;
2) Legibilidade: outro programador que ler esse código (ou até você mesmo, depois de um tempo) terá mais facilidade em compreender o fluxo;
3) É bem provável que você queira fazer algumas validações antes de persistir o objeto, conforme o comentário depois do try no código acima;
4) É possÃvel implementar testes unitários para esse método de forma mais simples.
Espero ter ajudado.
Abraço!
Olá professor, tudo na paz?
André, gostaria que você me tirasse uma pequena dúvida. O MVC já foi protocolado como três camadas, isso?
Model + View + Control
Quando acrescentamos mais uma camada, no caso a DAO, não estamos infligindo o padrão MVC? Apesar que no seu exemplo mostrado, não tem a camada DAO. Apenas para eu tomar um melhor norte na minha forma de programar. Obrigado mas uma vez.
Olá, Matheus, tudo certo?
Ótima pergunta! Com a camada DAO, muitos adquiriram o hábito de escrever “MVC + DAO” em artigos e postagens. Mas isso já faz alguns anos.
Atualmente, com a evolução de conceitos e ferramentas, o MVC passou a representar apenas uma base de padrão de arquitetura, portanto, você pode estendê-lo da forma que for melhor para o seu projeto. Por exemplo, hoje é comum encontrar arquiteturas que não só possuem a camada DAO, mas também camadas de repositório, serviço e segurança.
Logo, não estarÃamos inflingindo o conceito do padrão. Apenas estamos o expandindo para que possa ser encaixado em nossas necessidades.
Abraço!
André, você tem algum treinamento? curso online? To precsando aprender um pouco mais sobre, POO, MVC, interface, testes automatizados. Abraço!
Olá, Daniel, bom dia.
Infelizmente não ofereço cursos, mas você consegue encontrar conteúdos sobre MVC em portais de outros MVPs, como o Thulio Buttencourt.
Abraço!
Olá André, saiba que até a data de hoje 18/08/2023 as 00:48:nn esse post tem salvado a vida de muitos desenvolvedores, inclusive eu estou nesse pacote. Parabém por esse excelente material, muito obrigado, créditos e reconhecimento dedico a você.
Olá, Everaldo.
MuitÃssimo obrigado pelo comentário!
Fico muito feliz em saber que o artigo lhe ajudou.
Futuramente pretendo fazer um “remake” desse artigo, já que ele está muito antigo.
Abração!