Arquitetura MVC no Delphi

Arquitetura MVC no DelphiNada 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!

 

O objetivo principal deste artigo é mostrar a hierarquia 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 “visao”.
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:

Estrutura de pastas exibida no Project Manager do Delphi
Estrutura de pastas exibida no Project Manager do Delphi

 
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:

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

procedure TControleCliente.Salvar(const objetoCliente: TCliente);
begin
  // aqui devem ser escritas as funções de validação
 
  objetoCliente.Salvar(objetoCliente);
end;

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

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.

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:

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


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

80 comentários

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

  2. otimo trabalho
    esta sendo de muita valia, value.

    caro amigo,
    como ficaria usando datasnap 2010
    tem algum exemplo.

    obrigado.

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

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

    1. 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!

  4. 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!

    1. 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!

  5. 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!

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

    1. 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!

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

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

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

    1. 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!

  9. 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?

    1. 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!

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

    1. 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!

  11. 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)?

    1. 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!

  12. Quais as principais vantagens e desvantagens entre programar com DAO (Data Access Object) e ORM (Object Relational Mapping)?

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

  13. 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!

    1. 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!

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

    1. 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!

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

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

    1. 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!

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

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

  19. 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…

    1. 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!

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

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

    1. 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!

  22. Meus Parabéns pela iniciativa, e muito bom adquirir sempre mais conhecimentos e o melhor ainda e compartilhar com todos.
    Continue sempre assim.

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

    1. 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!

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

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

    1. 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!

  26. Olá,André.

    O conceito MVC, classes, polimorfismo, abstração aplica-se ao Delphi e em PHP?

    1. 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!

    1. 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!

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

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

      var
        objCliente: TCliente;
      begin
        objCliente := TCliente.Create;
        try  
          ClientDataSet1.Data := objCliente.ConsultarClientes;
        finally
          FreeAndNil(objCliente);
        end;
      end;

      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: contato@andrecelestino.com.

      Abraço!

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

    1. 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!

  29. 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!

  30. 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!

    1. 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!

  31. 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; ?

    1. 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!

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

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

      function TDAOCliente.ConsultarDados: olevariant;
      var
        DataSet: TClientDataSet;
        Provider: TDataSetProvider;
        Query: TSQLQuery;
      begin
        // cria os objetos para consulta no banco de dados
        DataSet := TClientDataSet.Create(nil);
        Provider := TDataSetProvider.Create(nil);
        Query := TSQLQuery.Create(nil);
        try
          // liga o Provider à Query, e o DataSet ao Provider
          Query.SQLConnection := SQLConnection1;
          Provider.DataSet := Query;
          DataSet.SetProvider(Provider);
      
          // executa uma consulta
          Query.SQL.Add('Select * from CLIENTES');
          Query.Open;
          DataSet.Open;
          
          // retorna a propriedade Data, que é do tipo "olevariant"
          result := DataSet.Data;
        finally
          FreeAndNil(DataSet);
          FreeAndNil(Provider);
          FreeAndNil(Query);
        end;
      end;

      A camada de controle, por sua vez, teria o mesmo comportamento do código do artigo:

      function TControleCliente.ConsultarDados: olevariant;
      var
        objetoDAO: TDAOCliente;
      begin
        objetoDAO := TDAOCliente.Create;
        try 
          result := objetoDAO.ConsultarDados;
        finally
          FreeAndNil(objetoDAO);
        end;
      end;

      Na View, finalmente, basta chamar o método da classe de controle:

      var
        objetoControle: TControleCliente;
      begin
        objetoControle := TControleCliente.Create; 
        try
          ClientDataSet1.Data := objetoControle.ConsultarDados;
        finally
          FreeAndNil(objetoControle);
        end;
      end;

      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!

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

    1. 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!

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

    1. 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!

  35. 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?

    1. 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!

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

    1. 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!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.