Matéria da MJ17: Desenvolvendo Sistemas OO com Padroes de Negócios
56 respostas
grprado
Primeira coisa gostaria de dar os parabéns ao Phillip pela matéria.
Mas como toda boa matéria ela também me deixou com algumas dúvidas:
Um POJO precisa saber como ficar num estado consistente? Ou ele deve delegar isso a um outro objeto?
Somente exemplificando, em livros de O.O. é pregado que um objeto tem sempre que estar num estado válido e que o construtor dele deve se encarregar disso. Já pela convenção JavaBeans e até Pojos, vejo que existem brechas que PODEm deixar o objeto num estado inconsistente.
Pelo que entendi da matéria os objetos devem sempre estar num estado consistente e eles próprios devem se certificar de que isso aconteça.
Se sim, caso seja fornecido um argumento invalido para o construtor ou para um setter o melhor seria dar um throw numa IllegalArgumentException com uma mensagem bem animadora?
Pergunto isso, pois em vários lugares vi que POJOS praticamente não sabem o porquê vieram ao mundo, ja na matéria, pelo que entendi, os objetos sabem muito bem que espaço devem ocupar.
Primeira coisa gostaria de dar os parabéns ao Phillip pela matéria.
Obrigado
Mais que POJOs, objetos de uma maneira geral devem garantir seu estado. Programar com contratos (DBC - Design by Contract) reforça isto através de algumas regras. uma pequena introdução á DBC pode ser encontrado aqui.
Note que mesmo numa linguagem totalmente integrada com DBC como Eiffel pdoe acontecer de um objeto não amnter consistência. O programador deve garantir a validade do estado do objeto. Linguagens como a citada Eiffel provêem recursos para facilitar esta verificação, em Java não tem muito jeito, é no braço mesmo.
Não conhecia DBC, mas já imaginava algo do tipo e que não seria muito fácil.
Quanto aos fantoches, o grande problema é que em muitos lugares eu via algo resumidamente deste tipo:
Camada de persistência retorna um pseudo-objeto (um objeto java travestido de struct, somente dados sem comportamento)
Ai a camada de negocios lida com o pseudo-objeto e vários objetos super fodões dessa camada fazem as operações de negocio e implementam externamente um compartamento interno do pseudo-objeto, por fim devolvem os dados para o usuário ou para serem persistidos novamente.
Era nesse ponto que eu me perdia, pois isso de O.O. não tem nada.
Após ler os artigos, MJ17 e do seu wiki, acredito ter encontrado um bom caminho para trilhar. Mais uma vez, obrigado.
Gerson
Realmente muito legal o artigo da revista.
Parabéns!
JUZAM
Opa, tenho uma pergunta sobre a mesma matéria, dentro do mesmo contexto.
Seguinte, no exemplo da listagem 1, tem um factory method para criar o cliente. Eu gostei muito desse approach pois caso a criação do objeto seja complexa a mesma fica bem encapsulada e cria o obj com um estado válido.
Mas como ficaria se fossem mais argumentos, ou seja, o cadastro de cliente tivesse uns 20 campos. Não da para passar String por String…
Qual seria uma forma legal de se fazer isso?
Eu pensei em algumas:
:arrow: Um Hash map com os argumentos.
:arrow: No caso de um app web (o meu caso) passar o form bean (Struts), ou managed bean(JSF).
:arrow: Começar a criar o obj na action, a parte simples tipo setNome e etc, e passar esse quase obj para o factory method terminar o serviço.
Cada um tem sua desvantagem, falta de verificação em tempo de compilação, acoplamento, e criar um obj com estado inválido.
Qual a opinião de vocês?
P
psevestre
[JUZAM:
]
:arrow: Um Hash map com os argumentos.
Como vc. notou, todas têm problemas mas esta é a que eu utilizaria. Outra opção ainda é ter um construtor que receba um POJO de dados de inicialização, que, por contrato, não teria que manter nenhum tipo de consistência entre parâmetros.
De qualquer forma, acho que o problema está no requisito “objeto consistente em qualquer momento”. Este requisito parece fazer todo sentido quando visto de longe, mas não sei se faz sentido mantê-lo a qualquer custo.
Na prática, creio que basta garantir a validade de invariantes em determinados momentos, em particular nos momentos em que existe efetivamente um contrato a ser obedecido, como em uma chamada de um método de negócio. AOP neste caso vem bem a calhar, pois pode ser usada para injetar a validação do estado apenas nos momentos em que isto é relevante.
Fabricio_Cozer_Marti
O conceito de contrato implementado em Aspectos é bem útil e relevante.
Algum tempo atrás, ainda não se usava muito AOP, e fazer validações no início de métodos para verificar se um objeto veio com todos os campos necessários para realizar tal processo era entrelaçado no código, isso rolava muito no projeto na época pq haviam muitas integrações, diversos sitemas interoperando, e os contratos foram implementados na época de forma entrelaçada …
Se fosse hoje em dia AOP era sem dúvida alguma a melhor pedida!
JUZAM
Exatamente. Digamos que a invariante de um obj dependa da camada de aplicação, por exemplo, um relacionamento com um objeto proveniente da camada de persistência. Então nesse caso faz muito sentido o factory method, onde a criação ficará encapsulada em um único local.
Neste caso, acho que não faz sentido criar o objeto sem o relacionamento no managed bean, e depois enviá-lo para a camada de aplicação fazer o relacionamento.
Eu opto pelo hash map ao estilo Ruby
brunohansen
[JUZAM:
]Opa, tenho uma pergunta sobre a mesma matéria, dentro do mesmo contexto.
Seguinte, no exemplo da listagem 1, tem um factory method para criar o cliente. Eu gostei muito desse approach pois caso a criação do objeto seja complexa a mesma fica bem encapsulada e cria o obj com um estado válido.
Mas como ficaria se fossem mais argumentos, ou seja, o cadastro de cliente tivesse uns 20 campos. Não da para passar String por String…
Qual seria uma forma legal de se fazer isso?
Eu pensei em algumas:
:arrow: Um Hash map com os argumentos.
:arrow: No caso de um app web (o meu caso) passar o form bean (Struts), ou managed bean(JSF).
:arrow: Começar a criar o obj na action, a parte simples tipo setNome e etc, e passar esse quase obj para o factory method terminar o serviço.
Cada um tem sua desvantagem, falta de verificação em tempo de compilação, acoplamento, e criar um obj com estado inválido.
Qual a opinião de vocês?
Acho que para criação de objetos complexos ou até povoar objetos seria interessante usar um builder!
Se alguém quiser discordar ou acrescentar algo!
rodrigoy
É uma má idéia a camada de apresentação acessar diretamente o repository?
JUZAM
Acredito que depende do caso.
Se for somente para montar um combo de opções, não vejo problemas.
Ou seja, não deve existir nenhuma regra de negócio envolvida.
pcalcado
Repositórios são objetos de domínio e anda impede que sejam manipulados pelas Camadas de Aplicação e até Apresentação.
O ponto importante é que repositórios não são DAOs, então sua Camda de Aplicação/Apresentação deve tratar Repositórios como trata (baseado no exemplo da revista) Proposta, Emprestimo, etc.
Um erro que cometi no artigo foi colocar um método salvar() no Repositório. Idealmente Repositórios devem ter uma interface parecida com a de Collections, em vez de salvar() e apagar() pense em adicionar() e remover().
Sendo assim, você poderia fazer um Service retornar um Repository. Eu não recomendo fazer este processo sem um Service porque você perde os benefícios deste ser um Façade, que incluem ter uma interface mínima. Se você expôr os métodos do Repository como interface da Camada de Negócios vai acabar com muitos métodos que quase nunca são usados, interfaces de Camadas em geral e Services em particular devem ter granularidade grossa.
JUZAM
Shoes, e sobre a pergunta acima com as formas de passar os dados da camada view para a camada de serviços.
O que você acha?
brunohansen
Uma coisa que não me sinto confortavel é fazer com que objetos de negocio tenham contato com repositórios.
Será que tem uma forma boa de fazer com que objetos de negocio não tenham contato com o repositório?
Será que persistir dados é um problema de negocio???
Será que persistir dados é um problema de aplicação???
Se for de negocio justifica meus objetos de negocio terem contato com o repositorio, agora se for um problema de aplicação já não justifica muito…
Se alguém puder comentar sobre esse problema e me ajudar a refletir melhor…
abraços…
Ahhhhh Shoes parabéns pela matéria! Fowler na mente!
pcalcado
Repositórios não tem a ver com persistência. O fato de geralmente serem relacionados com a Camda de Persistência é um detalhe de implementação, como falei acima Repositórios não são DAOs.
Repositório é o lugar onde você acumula seus objetos, são coleções, listas.
Neste caso:
classUsuarios{
Set<Grupo> grupos = null;
}
É o mesmo princípio.
O ponto é que geralmente Repositórios contêm lsitas de todos os objetos de um determinado tipo, logo dificilmente um Usuario teria um RepositorioGrupos, já que este repositório não guarda apenas os eus grupos mas todos os do sistema.
Um GerenciadorUsuario, entretanto, pode ter acesso ao repositório já que ele precisa saber onde ficam todos os grupos do sistema.
rodrigoy
O caso seria o que o JUZAM falou. Quero só obter dados para popular a tela.
O repositório não é DAO, mas é responsável pela “manutenção” dos Domain Objects como se eles estivem sempre em memória (a implementação do Repository pode ser o próprio DAO certo?). Imagine que eu queria encapsular todas as buscas no próprio repository (que é quem sabe dos dados). Veja o pseudocode.
Sobre o service, não sei o que é pior, ter operações que nunca são chamadas ou fazer uma fachada que só delega coisas. Entre essas estou tendencioso a deixar as coisas mais simples. Para buscas chamar o Repository direto.
E sobre a granularidade, isso me causa dor de cabeça. Tinhamos uma prática aqui de 1 Service para cada Caso de Uso (não sei de onde surgiu). Achei essa abordagem ruim. Ela deixa claro as funcionalidades envolvidas no caso de uso, mas gera muita delegação desnecessária (principalmente para buscar as coisas).
Obrigado pelas sugestões e vamos continuar com a discussão. Aliás, tenho dúvidas de onde seria melhor colocar o controle de transação nesse caso…(muito tempo em análise faz mal).
pcalcado
Pois é, o que eu sugeri foi o segundo ‘B’
Com o Repository na mão você não precisa expôr métodos de pesquisa no Service. De qualquer forma usar o Repository fora da Camada de Negócios só para consultas (em algums casos extremos você poderia ter uma interface que seus clientes conhecessem e usassem apenas com métodos de consulta…).
Quando a granularidade, ter um caso de uso por Service não é a única opção, tudo depende do nível que você quer. Eu refatorei um sistema que implementava Services como Commands e pelas minhas cotnas haviam mais de 220 Services (que, para piorar, eram EJBs).
Existem algumas herurísticas apra descobrir uma granularidade legal, uma das mais interessantes que já vi é a do livro UML Components, mas mesmo essa não se aplica a tudo (principalmente por se basear demais em casos de uso e seus passos e fluxos). É muito relativo.
Quanto á transações, marque-as para terminar no final do caso de uso. Usar transações programaticamente em 2006 é furada, use Spring, EJB 3.0, AOP ou alguma coisa que permita tirar este código da sua regra de negócio. Se não tiver jeito de usar um bom framework, coloque este código commit/rollback na Camada de Aplicação.
rodrigoy
É segundo B, maldito copy n’ paste.
Imagino que no repository, fora adicionar(obj) e remover(obj), basicamente o resto seriam buscas que não seriam arriscadas expor para as camadas mais altas.
Estou pensando em fazer um repositório genérico que sabe guardar coisas com as operações acima e os outros repositórios terão buscas.
Dessa forma não haveria risco do RepositorioClientes estar diretamente na camada de aplicação.
Pela minha experiência acho ruim amarrar qualquer tipo de referência no código ao caso de uso (eg. ManterClienteService). Caso de uso não é componente. Esse refactorings nos façades sempre vão ter…
pcalcado
Sim, mas Casos de Uso delimitam as transações, componentes não. De qualquer modo você sempre pode marcar Services como REQUIRED e ter uma gestãod e transação eficiente sem precisar de muito aborrecimento. Aliás desta forma você elimina também a necessidade de métodos save() e delete(), desde que utilizando uma infra-estrutura baseada num framework moderno, como Hibernate, ou até mesmo em EJBs 2.x.
Para buscas, existe um padrão de Eric Evans/Fowler chamado Specification que basicamente verifica se o objeto em questão obedece um certo critério. Como uma Specification é um objeto de domínio você pode utilizar o padrão do Fowler chamado QueryObject para implementá-lo, como um DAO implementaria um Repository um QueryObject implementa uma Specification. Isso elimina métodos do tipo returnAll() no Repository, basta um método que aceite um Specification.
O
okara
Uma transação, mesmo que não implementada diratamente no código, não seria algo inerente a regra de negócio em questão.
Pelo que eu entendi, na orientação ao objetos eu teria objetos colaborativos.
ex:
A transação faz parte do próprio escopo da operação e não é uma coisa alheia a isso.
O
okara
Também adoto um padrão parecido, onde possuo objetos de selecção ao invés de métodos para diversos critérios.
O uso da transação com autosave é realmente muito bom, mas também acho que é confuso para nossas mentes que trabalham linearmente com o processo onde no fim “as coisas são salvas” e quem manda salvar é alguém. Determinados requisitos necessitam que as coisas sejam salvas no momento determinado pelo usuário. Nesse caso, tenho dúvidas da aplicação do autosave.
Digo isso pelo seguinte: imagine um cliente desconectado (Swing). Obtive do servidor uma lista de clientes para alterar o nome numa grid, mas só deve mudar mesmo quando eu clicar no salvar. Nesse caso, o salvar é explicito. Essa arquitetura resolveria? (essa é uma das razões para a minha preocupação com a transação)
Outra coisa é que não sei ao certo se é uma boa idéia deixar o Domain Model dependente da arquitetura. Será que não pode ocorrer um “downgrade” da arquitetura que pode me dar problemas? (só pensamentos my friend)
Vejo que a aplicação do QueryObject é bem específica para aquelas buscas por diversos parâmetros que podem ser opcionais. Não se aplica a tudo. Se for pensar assim os repositories só teriam uma operação o que semanticamente os deixariam “bem burros”. Eu aplico QueryObject em “buscas com parâmetros diferenciados”. Não sei se existem soluções mais fáceis, mas a implementação do QueryObject também causava náuseas nos programadores quando começava a se falar nos relacionamentos.
Pensando um pouco nos impactos gerados no processo:
Você concordaria comigo que o trabalho do “analista, designer ou o que for” é chegar no Domain Model? A partir dali os “programadores, codificadores ou o que for” poderiam tocar com uma grande parte dos requisitos garantidos? (lógicamente é preciso ter uma arquitetura definida que atenda esse nível de abstração).
pcalcado
Eu não falei que era relativa a regra de negócio (apesar de poder ser). O que eu disse foi:
Um componente, por definição reutilizável, não pode fazer suposições sobre quando transações começam ou terminam, um Caso de Uso pode servir como limite.
Seu exemplo não me permite utilizar a classe Venda em um contexto não-transacional, além de lidar com requisitos não-funcionais misturados com código de negócios.
O
okara
Mas o código da transação não precisaria estar embutido na lógica de negócios. (para isso temos AOP, etc)
O método efetivar apenas delimita a operação como um todo, ou seja, para efetivar uma venda eu preciso adicionar uma venda e atualizar o meu estoque, no caso do exemplo.
O
okara
Isso é um dos problemas da implementação desse padrão.
Também me perdi nisso.
Mas você ter um repositorio muito inteligente não dispensaria o uso de um objeto Manager (gerenciador) ?
É essa minha dúvida.
pcalcado
Isso é uma regra de negócios, logo você misturou funcional e não-funcional
Como falei em alguns posts o ideal é extrair isso utilizando AOP diretamente ou (melhor) um framework
rodrigoy
O repositório é uma interface. Quem é o manager? Não entendi a sua pergunta…
O
okara
O Gerenciador é isso que o shoes citou.
pcalcado
rodrigoy:
O uso da transação com autosave é realmente muito bom, mas também acho que é confuso para nossas mentes que trabalham linearmente com o processo onde no fim “as coisas são salvas” e quem manda salvar é alguém.
Sim, mas isso exige um maior treinamento em OOP, creio.
Eu concordo.
Assumindo que a grid seja um objeto em memória, não há problemas, mas se grid for um daqueles “adoráveis” componentes que trazem uma tabela para um formulário vai depender de quando seu caso de uso termina. De qualquer forma o grid e seus mecanismos possuem um potencial de reutilização baixo e pode fazer algumas suposições sobre como vai ser executado (geralmente componentes de Apresentação e Aplicações têm esta característica).
Arquiteturas são por definição decisões que são difíceis de serem alteradas, mas supondo que o sejam não consigo pensar num modelo masi livre da arquitetura do que este. Veja o domain model proposto, eel não depende em nada de Hibernate, JDO, JDBC, Spring… o que ele precisa são serviços que devem ser supridos por alguém como transações e persistência. Onde você acredita que possa haver dependência?
Eu não sou contra Repositorios com metodos de pesquisa, foi apenas uma sugestão apra que você pudesse ter uma interface única para todos os seus repositórios.
De qualquer forma, ter apenas um método não significa que um objeto é burro. A função do repositório é aplicar a especificação nos seus objetos (que acaba sendo implementado por um DAO aplicando um QueryObject na persistência), ele tem diversas responsabilidades.
Da mesma forma, nada impede que você tenha uma Especificação que se traduz em “SELECT * FROM BLABLA”. O bom de usar especificações é que são conceitos de domínio, ams realmente na maioria das vezes utilizar métodos para busca é mais simples, porém, novamente, foi apenas para deixar seus repositórios gnéricos mais completos
Sim e não, mas isso tem mais a ver com metodologia e processo.
Deixa eu explicar o que estou tentando fazer com meus analistas e programadores (sim, meu projeto atual tem uma diferença enorma -geograficamente falando, inclusive- entre analistas/projetistas e programadores). Eu tenho 6 analistas que vão trabalhar basicamente com o domain model e testes unitários. Basicamente eles fariam isso (se o modelo de fábrica de sofwtare funcionasse, como não funciona eles vão fazer muito mais, mas este é o papel original).
Como eles vão produzir um Domain Model pronto e testado, totalmente baseado em Repository, Specification, Service e tudo mais, cabe aos programadores e a mim como arquiteto a implementação dos serviços que dão suporte ao Domain Model. Assim nós criamos os DAOs segundo a especificação de contrato dos repositórios deles, fazemos os services serem expostos como EJBs e WebServices e por aí afora.
Ter o Domain Model como POJOs de negócio usando padrões de domínio permite testar regras d enegócio o tempo todo.
O
okara
Qual seria a diferença ?
Não tenho essa idéia muito clara ainda.
pcalcado
Não. O Service basicamente traz um processo de negócio, um fluxo de atividades, um Repositório apenas armazena (agrupa?) objetos.
O
okara
No caso você quer dizer que um repositório armazena objetos de tipos definidos ex : VendasRepositorio agrupa apenas objetos vendas ?
E um Service (Manager, não sei se posso chamar assim também) pode conter uma operação que possui um fluxo de atividades com objetos colaborativos ?
É isso ?
pcalcado
Pensando melhor na sua dúvida algumas funções de um Service podem ser feitas por Repositories, basicamente as de consulta e isso volta ao papo que eu tava tendo com Rodrigo sobre expôr métodos dos Repositories.
Nada impede que você tenha um repositório Vendas, outro VendasAVista e ouro VendasCanceladas, por exemplo, mesmo que um seja superconjunto do outro. Basta saber se vai agregar algum valor ao seu sistema (e você pode, inclusive, ter um DAO só implementando as interfaces dos múltiplos Repositórios se for o caso…)
pcalcado
okara:
E um Service (Manager, não sei se posso chamar assim também) pode conter uma operação que possui um fluxo de atividades com objetos colaborativos ?
Não entendi, exemplo?
Se for um Service (ou Manager) colaborar com outro, ´há quem não veja problema e há quem diga que deveria haver um Mnagaer de granularidade mais grossa controlando a interação. Eu acho que na maioria dos casos não há problema (um Service não é necessariamente um componente antes que alguém doido por CBD queira minha cabeça) na interação entre dois Services, por exemplo o de usuarios fazendo consultas através ao de grupos para criar um novo objeto.
O
okara
Deixa para lá essa dúvida.
Mas, poderia substituir meu DAO por um Repositorio ?
E o operações de vário objetos seriam feitas por um repositorio.
Por exemplo, teria um método
ou por um manager
pcalcado
[JUZAM:
]
Mas como ficaria se fossem mais argumentos, ou seja, o cadastro de cliente tivesse uns 20 campos. Não da para passar String por String…
Qual seria uma forma legal de se fazer isso?
…
:arrow: Começar a criar o obj na action, a parte simples tipo setNome e etc, e passar esse quase obj para o factory method terminar o serviço.
Se sua invariante não depender de muitos destes parâmetros acho que criar o objeto na Factory passando o mínimo para obedece-la e popular o resto diretamente no objeto é a melhor maneira. Em termos de contratos não faz muita diferença se quem popula é o cliente ou a fábrica,d esde que mantida a invariante.
Se o seu objeto depender dos 20 parâmetros apra sua invariante, divida ele em mais objetos porque tem algo errado aí Uma invariante deve ser mínima, eficaz e eficiente.
Não podendo dividir por um motivo X (coisa bem rara, BTW) eu acho que adotaria o Map mesmo. Se checagem em tempo de compilação te fizer falta você pode criar um Prototype(GOF), mas se você já criar um objeto na mão não faz muito sentido usar um Factory, a menos que sua Factory faça coisas como dar ao objeto referêncais apra outros objetos de negócio encapsulados nele ou coloque ele em modo attached, por exemplo.
Ou você pode usar uma Specification um pouco diferente do conceito original. Em vez de dizer: eu quero um objeto já existente com essa característica você poderia dizer: crie um objeto pra mim que passe nesta especificação.
pcalcado
Acho que você quis dizer Service/Manager, né?
Poder pode, mas eu não faria isso, não criaria Métodos com efeitos colaterais em um repositório.
Se você tem um processo em batch e quer abstrair uma Stored Procedure, por exemplo, pode ter um método no Repositório que aceite e execute um Command (que, em sua implementação, and amais faz que chamar uma SP via DAO) mas este fluxo deve começar no Service.
rodrigoy
Fundamentei o exemplo na situação do Domain Object “Cliente” não ter uma operação salvar() pois ele usa o autosave do container. Nesse meu exemplo o que o botão salvar chamaria?
Digamos o seguinte: se eu não colocar as operações “salvar()” nas minhas Domain Classes faço isso porque a minha arquitetura permite o “autoSave”. Correto? Estou partindo da abstração que os objetos se salvam sozinhos quando “mexidos”. E se eu dar um downgrade na minha arquitetura para uma que não permite isso?
(é um casinho meio estúpido, nem precisa responder, mas é só para exemplificar que o Domain Model não é realmente dependente da arquitetura, mas é dependente das facilidades que a arquitetura proporciona). Isso dá margem para escrever “Refactoring Domain Model”, ha ha ha ha…
OK! É precisamos tomar cuidado com essas decisões… Imagine se os seus “analistas/designers” que estão lá longe no seu projeto decidam só usar QueryObject :? (afinal a análise fica mais simples, o programador é que se vire para implementar).
Acho a abordagem de vcs muito acertada. Como eles demonstram a apresentação ou os boundaries (conversa ApAp <-> Domain).
Você critica o funcionamento de fábricas em geral ou particularmente a sua?
pcalcado
Depende. Ele éremoto? Se for chame o método no servidor que atualiza. Ele é local? Então não vejou outra opção além de chamar uma abstração para commit.
Pois é, são os serviços que a arquitetura assume estarem disponíveis, porém não amarrados a uma tecnologia em específico. Mudar isso implica mudar a arquitetura. Uma mudança mais amena poderia utilizar AOP para chamar o save() ou na pior das hipóteses fazer na Camda de Aplicação ou de Apresentação, mantendo ainda o Domain Model limpo dentro do possível.
No caso aqui o maior problema na verdade é lazy loading, esta sim é uma decisão arquitetural difícil de remover.
Por este tipo de coisa que eu não acredito em design separado de implementação para linguagens de alto nível modernas
mas de qualquer forma não consigo pesnar em algo tããão grave que possa ser feito com uma Specification. Além disso, sendo Specification uma abstração não necessariamente ela vai ser um QueryObject. Que tal?
publicclassListarUsuariosSpecimplementsSpecification{privateUsuarioDaodao=null//injete aqui seu DAOpublicList<Usuario>aplicar(){returndao.listarusuarios();}}
Hm… não entendi, foi uma pergunta?
Em geral. Nunca vi o modelo 9a) funcionar (b) produzir algo de qualidade. O modelo talvez funcione se seguido a risca os princípios de Engenharia de Sofwtare mas eu sou do time que acha que Engenharia de Sofwtare (metodologia do SEI criada na OTAN) só serve, e mesmo assim talvez, apra projetos muito grandes geralmente onde o ahrdware é mais importante que o software e vai ser desenvolvido em apralelo.
Para gestão de informação essas práticas nunca me mostraram resultados reais, eu só vejo 9em projetos que participei ou não) desculpas sobre porque a metodologia não dá certo e promessas que da próxima vez vai ser diferente.
Outro dia tive que ouvir um " a documentação está defasada em relação ao código porque brasileiro não tem cultura de fazer design". Como se desse certo em algum outro país…
rodrigoy
pcalcado:
No caso aqui o maior problema na verdade é lazy loading, esta sim é uma decisão arquitetural difícil de remover.
Nem me fale… esses frameworks como o Hibernate resolveram um problemão… implementar Proxies (como exemplo) é uma pentelhação. Realmente no caso de um downgrade tão violento assim daria vontade até de pular fora do projeto… :shock:
É, é uma pergunta. Como os seus designers do projeto passam a ligação entre as boundary classes e o domain? Fazem um SD?
Comentei que a abordagem é acertada pois sempre temos um problema quando o designer entrega o modelo UML diretamente para a codificação. Da forma que vocês estão trabalhando o modelo (que no caso é o próprio código) é testável, coisa que a UML não é (não me venham com diagrama de objetos). Não é uma crítica à UML, mas sim aos designers. Eles devem aplicar a boa prática de continuamente testar o software e não se esconder atrás do modelo.
pcalcado:
Em geral. Nunca vi o modelo 9a) funcionar (b) produzir algo de qualidade. O modelo talvez funcione se seguido a risca os princípios de Engenharia de Sofwtare mas eu sou do time que acha que Engenharia de Sofwtare (metodologia do SEI criada na OTAN) só serve, e mesmo assim talvez, apra projetos muito grandes geralmente onde o ahrdware é mais importante que o software e vai ser desenvolvido em apralelo.
Para gestão de informação essas práticas nunca me mostraram resultados reais, eu só vejo 9em projetos que participei ou não) desculpas sobre porque a metodologia não dá certo e promessas que da próxima vez vai ser diferente.
Outro dia tive que ouvir um " a documentação está defasada em relação ao código porque brasileiro não tem cultura de fazer design". Como se desse certo em algum outro país… :P
Tem que levar em consideração que é difícil achar um gestor que saiba aplicar a metodologia certa no projeto certo (geralmente ele adota o WUP - Waterfall Unified Process). Faço uma crítica muito grande aos gestores de projetos de software em geral.
Documentação é uma palavra que não posso mais ouvir. Já acompanhei duas implantações de CMM. Não ví ganho nenhum na implantação da certificação (fora a churrascada grátis quando pegamos o “selo”), o processo ficou pesadíssimo e Cataratas do Iguaçu. Ví um repositório com 500mb de documentação.
Eu gosto de modelar usando a UML, mas uso a UML como ferramenta de análise. A UML é para descobrir informações. É o que tento explicar para os meus alunos. Não sou “xiita” ao ponto de jogar fora os diagramas (Fowler). Estou para fazer uns testes. Imagino que se o designer se focar em modelar em UML somente o Domain Model suas ligações com as Boundaries os problemas na sincronização do código com o modelo podem ser bem minimizadas, fora que esse designer seria independente de plataforma, arquitetura e etc… (mas é só um teste, acredito que para uma fábrica hibrida JAVA/.NET isso pode ser bom, o designer se encaixa em qualquer projeto sem conhecer as entranhas da arquitetura).
Edufa
Duas dúvidas:
Qual a diferença conceitual entre o Service e o Facade, ali quando é apresentado o GerenteConta como um Service, ele não poderia ser tb um Facede por definir um ponto de entrada?
Acredito que entendi o conceito do Repositório, mas fiquei um pouco perdido em como viabiliza-lo, e quem é responsável pelo o que: Usando o exemplo da revista, eu tenho um repositório de categorias, quem é responsável por criar este repositório, seria a Facade? A Facade busca uma categoria no repositório, se não encontrar, o repositório busca no DAO? E ao criar uma nova categoria, primeiro coloca ela no repositório e é ele o responsável por salvar fisicamente (usando o DAO).
[]s
rodrigoy
Vamos lá: Um Façade é uma fachada, um filtro, ele diz o que pode e o que não pode entrar na camada de negócio e também diz o que pode sair. Um facade impede algo do tipo:
Viu só que meleca? Estou jogando um ActionFormDoStruts pra dentro da minha camada de negócios, isso diz que a camada de negócio precisa conhecer o struts para funcionar (péssima idéia). Pior, esse método está
retornando uma classe de infra-estrutura, indicando que a minha camada de aplicação precisa conhecer a infra (uma idéia pior ainda).
Outra coisa que o Phillip colocou no artigo e que já discutimos aqui é que o façade tem granularidade grossa e agrega várias funcionalidades de granularidade fina que estão na camada de negócios.
O service é uma classe de negócio que coordena como as coisas acontecem. Ele tem uma inteligência própria que é estranha a qualquer outra entidade do Domain Model. No exemplo do Shoes, você tem conta1, conta2 e um valor. Ele fez um service que recebe isso e faz o trabalho. Nesse exemplo, não teria grandes problemas fazer um método conta1.transferirPara(conta2, valor) com essa inteligência.
Mas, determinadas operações ficam bem estranhas estarem nos entities. Exemplo: Quando você fatura um pedido, uma nota deve ser impressa, um estoque deve ser baixado, o crédito do cliente deve ser atualizado e mais umas trocentas coisas devem acontecer. Se você colocar tudo isso dentro de pedido.faturar() o próprio pedido vai ter que coordenar COMO todas essas outras classes fazem as suas tarefas diminuindo a coesão. Fora que dependendo do contexto, não é bom que o pedido conheça essas classes.
Determinados problemas é melhor você ter um servico do que um método. É importante ressaltar que o Pedido.faturar() pode existir e chamar esse servico (nesse caso, Dependency Injection nele).
[]s
Rafael_Nunes
Uma dúvida em relação a integração/comunicação da camada view com a camada de aplicação.
A camada view por exemplo creio que não deve conhecer os objetos de domínio, mas como fazer as chamadas à camada de aplicação sem conhecê-los?
Um exemplo um pouco mais prático:
Neste caso quem chamar este método, deverá conhecer o objeto de domínio ‘Client’ para passá-lo, certo?
Mas se não tiver esse conhecimento, o método responsável é quem vai ter que conhecer e em contrapartida receber todas as informações.(Imagine se houver uma dezena de atributos, ter de passar todos como parâmetros)
Como vocês tem tratado isso?
O
okara
Essa pergunta é muito interessante.
E no caso de transmitir um conjunto de dados ?
exemplo: um list de Clientes
public List<Cliente> consultar(int codigo) {
}
Como fazer isso sem que a camada view conheça o objeto cliente ?
rodrigoallemand
Eu, particularmente, não vejo problemas em outras camadas utilizarem Objetos de Transferencia, como no caso que vc citou, alguem conhecer o objeto Cliente. Isso é uma ‘briga’ que já tivemos aqui na empresa nas várias reuniões de arquitetura…
Acho que chamar um método com 12 parametros, alem de ferir qualquer regra de arquitetura, fica praticamente inviavel…
Eu passo os objetos de transferencia (TO para uns, DTO para outros) entre as camadas e não vejo melhor solução…
Alguem tem uma saida para esta sinuca?
pcalcado
Num esquema de Camadas empilhadas a Camada de cime depende da interface da Camada de baixo, isso faz com que objetos que façam parte da interface da Camada de Baixo (como objetos de negócio quem entrem e saiam) possam ser utilizados.
Quanto à TOs, por que simplesmente não utilizar os objetos de domínio ao invés de copiar dados entre objetos, manter hierarquias paralelas, criar objetos de dados sem comportamento e todos os outros problemas de TOs?
Se você não quer que a Camada de cima tenha acesso à métodos específicos faça-a manipular uma interface. Exemplo:
Se eu não quero que minha Camada de Apresentação consiga chamar o método depositar (que é um método que muda o estado do Objeto e só deve ser chamado de outras Camadas) basta criar uma interface:
interface Conta{
public String getNumero();
}
Fazer C/C implementá-la e a Camada de Apresentação lidar apenas com ela. Sem repetiçãod e dados, sem objetos burros.
Note que mesmo esta solução é um tanto drástica demais, eu mesmo nunca vi real necessidade em meus projetos.
rodrigoallemand
O que eu quiz dizer foi:
Em uma consulta, por exemplo:
Meu Controller recebe uma interface Service a partir de uma fabrica
Meu Controller chama um método e recebe um objeto de dominio (DTO, que tb é utilizado para espelhar a minha tabela, por exemplo, no hibernate).
Em um caso de criação:
Meu controler recebe os campso do formulário e cria um DTO populado com tais valores.
Meu Controller recebe uma interface Service a partir de uma fabrica
Meu Controller chama um método e passa o objeto de dominio (DTO, que tb é utilizado para espelhar a minha tabela, por exemplo, no hibernate).
O que vc acha disso?
Rafael_Nunes
Ainda achei a criação de uma interface melhor do que ter VO´s espalhados pela aplicação.
Mas por exemplo se você não cria esta interface e deixa a camada de apresentação conhecer os objetos de domínio, o que impede de a camada de apresentação fazer uma chamada para um método que não deveria?
rodrigoy
Não vejo problemas (até agora não ví) da camada de aplicação ou apresentação conhecer e usar entidades do Domain. É exatamente esse sentimento que gera muitas coisas desnecessárias no projeto (TOS, DTOS) e tendem a levar ao código procedural (já sofri bastante com essas más definições arquiteturais, meu projeto atual está bem procedural e o refactoring disso vai levar mais de um ano, exatamente por essa tentativa dos arquitetos anteriores de isolar o negócio de tudo, pra quê?).
Se existir uma necessidade da interface que o Philip falou, será mais por questões de arquitetura do que por necessidade de negócio. Por exemplo, se um pedido pode ser faturado [pedido.faturar()], não seria a camada que ele se encontra que limitaria a chamada do método. O que limitaria seriam as regras de negócio do próprio pedido.
rodrigoallemand
Isso se aplica quando está executando rotinas que não tem objetos de retorno…
Neste mesmo exemplo, como vc colocaria o retorno da interface para o método getDetalhesCliente(idCliente)?
Fabricio_Cozer_Marti
Rafael Nunes:
Ainda achei a criação de uma interface melhor do que ter VO´s espalhados pela aplicação.
Mas por exemplo se você não cria esta interface e deixa a camada de apresentação conhecer os objetos de domínio, o que impede de a camada de apresentação fazer uma chamada para um método que não deveria?
Esse ponto que eu ia colocar, nessa forma você vai ter uma interface a mais, porém não vai impedir que haja quebra de encapsulação. E se a proposta for reduzir a complexidade, diminuir uma classe VO e adicionar outra interface, daria no mesmo, não ? :roll:
Rafael_Nunes
Uma sugestão, seria colocar as classes que compõe a camada de aplicação, no mesmo pacote das classes de domínio. Assim, os métodos que a view poderia acessar marcaria-os como públicos, e os que seriam acessíveis somente pela camada de aplicação marcaria-os como acesso default ou protected.
pcalcado
DTOs não são objetos de domínio, são objetos usados apenas apra transmitir dados entre Camadas Físicas, não tem nada a ver com o Domínio da Aplicação.
Você não precisa (e nem deveria) usar DTOs com Hibernate. Isso é uma herança de Entity Beans que não faz o menor sentido quando se usa uma ferramenta de ORM para POJOs.
Eu nunca vi necessidade disso mas se você precisa se precaver basta criar a interface.
Eu diria que esta interface em específico seria mais relacionado com a estrutura de desenvolvimento. Imagino que um projeto onde times diferentes desenvolvemas Camadas você queira se rpecaver da outra equipe trapacear nos métodos, mas como disse nunca vi acontecer com tanta importância.
rodrigoallemand:
Neste mesmo exemplo, como vc colocaria o retorno da interface para o método getDetalhesCliente(idCliente)?
Ué, faria um método na interface que retorna um objeto… não entendi sua dúvida.
Fabrício Cozer Martins:
Esse ponto que eu ia colocar, nessa forma você vai ter uma interface a mais, porém não vai impedir que haja quebra de encapsulação. E se a proposta for reduzir a complexidade, diminuir uma classe VO e adicionar outra interface, daria no mesmo, não ? :roll:
Por isso que eu falei que não é comum utilizar esta técnica e as diferenças mais básicas são que você não rpecisa manter duas hierarquias de lasses nem ficar copiando dados para objetos de transferência.
Acho que isso limitaria demais a escolha de hierarquia de pacotes, mais do que estou disposto, mas é uma saída…
rodrigoy
Phillip, vamos trocar mais experiências aí.
No seu artigo você comentou a respeito de Domain Model vs Transaction Scripts, algo do tipo “nem todo sistema exige a complexidade de um domain model… blá blá blá…”.
Bom, atualmente com mecanismos ORM um mapeamento fica mais simples. Isto é, é mais fácil atualmente ter uma estrutura OO persistente e convenhamos 90% dos projetos são aplicações de banco de dados. Se você tem o seu banco espelhado em objetos, potencialmente, já seria um Domain Model. Desse modo, não seria sempre mais fácil ter um Domain Model do que trabalhar com TranScripts?
Digo isso porque não acredito que é a complexidade que vai ditar o uso ou não de um domain model. Acho que para aplicações de banco de dados o Domain Model vai sempre existir, com doses maiores ou menores de TranScripts dependendo do caso.
Abraços!
Rafael_Nunes
Acredito que não, pois o Domain Model seria formado também por classes de negócios, que realizam processamento de use-cases e que não são obrigatoriamente espelhadas em tabelas.
rodrigoy
Acredito que não, pois o Domain Model seria formado também por classes de negócios, que realizam processamento de use-cases e que não são obrigatoriamente espelhadas em tabelas.
Isso não responde a minha pergunta. Que situação você teria Entities e Transaction Scripts que não justificam um Domain Model?
louds
Transaction Scripts e Entities são uteis quando você não tem uma aplicação rica em comportamento, com predominancia de crud por exemplo, e a maioria dos casos de uso são apenas manipulação do banco de dados sem muita lógica de negocio (sumariza todos registros X, insere na tabela Y e atualiza na Z).