Modelo de Arquitetura para Sistema de Médio Porte

Boa Noite pessoal!

Andei pesquisando bastantes nessas ultimas semana a respeito de arquitetura de sistemas, mas ainda estou com algumas dúvidas e por isso estou recorrendo ao GUJ.

Estou iniciando um projeto de médio porte, onde ocorrerão muitos acessos a informações contidas em banco de dados, este que atualmente esta com aproximadamente 10Gb de informação, e ele tende a crescer quando eu importar algumas informações referentes a dados de funcionários.

Minha dúvida é a seguinte, qual seria a melhor forma de tratar os acessos ao banco (selects, inserts, updates e deletes) dentro do sistema, não em momentos de cadastros, pois nestes casos as DAOs resolvem, mas sim nos processos do sistemas, tais como importações de arquivos, cálculos estatísticos, criação de estrategias e por fim geração de um PPT que está parametrizado na base de dados,

No caso descrito acima, não vejo DAOs como a melhor solução, pois nem sempre precisarei de todos os dados de um objeto, e na maioria dos casos serão selects com alguns JOINS.

Pensei em usar algo semelhante ao que usamos na empresa em que trabalho, mas com algumas melhorias de OO, pois o sistema em que trabalho hoje é um sistema desenvolvido em Delphi lá pelos anos 2000, mas temos casos muito parecidos, tais com cálculo de folha de uma empresa, e o fechamento de competencia, que são ações que demandam muitos acessos ao banco, e lá nas classes de negócio, por diversas vezes fazemos os acessos ao BD por elas.

Alguém tem alguma sugestão de como eu poderia seguir?

Obrigado!!

Os seus DAO’s não necessariamente trabalham para operações CRUD.

Com o Hibernate, por exemplo, você pode customizar o retorno de uma consulta ao banco de dados, de forma que uma classe específica receba todos os campos resultantes de sua consulta customizada (JOIN que traz campos de duas tabelas, por exemplo).

Referente aos SQL’s (QLs no caso da JPA), podem ser centralizados em algum arquivo .properties, por exemplo. Você também pode fazer esta customização com @NamedQueries, especificando a classe de retorno customizada em cada @NamedQuery. Enfim, o importante é ter em mente que as consultas podem ser customizadas como queira.

Espero ter ajudado! ^^

Eu montei um pedaço da infraestrutura e estou usando o Hibernate com as Native Querys, com essa parte estou até que tranquilo, o que me gera mais dúvida é a questão das diversas alterações na base que vão ocorrer.
Vou pegar com exemplo o processo de fechamento de uma folha de pagamento.

Temo neste caso o calculo dos valores a serem pagos/descontados, até ai de maneira geral são selects dos valores e select da rotina de cálculo que foi parametrizada, entretanto esse calculo ocorre para algumas centenas de funcionários, e após os cálculos, temos gravação de log, atualização do cadastro do funcionário, pois pode ter ocorrido a programação de alguma alteração, replica dos dados antes da atualização para manter histórico, gravação dos valores apurados no cálculo em tabelas de resultados e por fim emissão de relatórios.

No caso acima iniciamos uma transação para cada funcionário e ao final realizamos o commit ou o rollback caso tenha ocorrido algum erro.

Nesta situação, as DAO’s fariam esse controle de transação?

Ah, já ajudou um pouco sim… :slight_smile:

o dao tem a capacidade de abstrair o banco de dados. é util para desacoplar o seu programa da infraestrutura.

ele não vai fazer apenas controle de transações. se vc precisar de cache, é nesse cara que vai trabalhar (usando hibernate também).

banco de dados é um detalhe da sua aplicação. a logica de calcular uma folha de pagamentes é a mesma se os dados vem de um arquivo ou de 50 tabelas ( o salario sera o mesmo ).

é claro que o seu sistema não é totalmente desacoplado com a fonte de dados, vc vai ter que fazer algo, seja produzir log ou mesmo gravar no banco de volta quanto ja foi pago ( chute meu isso ).

nesse caso vc não precisa misturar o codigo com INSERT/ DELETE / UPDATE. vc deixa isso pro DAO.

uma outra vantagem são os testes unitarios: vc pode injetar um DAO ( um mock/stub ) que pode lançar exceptions ou que retorna determinados valores e vc pode verificar todos os fluxos ( cobertura ) do seu codigo.

por fim, se um dia vc mudar de banco/alterar colunas/etc, vc muda o DAO.

eu realmente não entendo pq vc gostaria de trabalhar sem uma abstração dessas ( pode usar Repositorios/DDD no lugar) a menos que o seu problema seja performance (e ainda assim isso é discutivel pois o overhead que vc tem é apenas uma indireção, uma chamada de metodo, se vc comparar com um profiler vai ver que o ganho sera de micro-segundos ).

se vc tem problemas de performance e o calculo de folha de pagamentos é algo paralelizavel e independente (o salario de A depende de algo em B?), eu tentaria escalar processando varios empregados ao mesmo tempo em threads diferentes ou usando algum tipo de job scheduler.

eis uma vantagem dos testes unitarios: vc pode garantir que a logica funciona em todos os casos previstos na especificação. se vc precisa executar em paralelo isso vai ser relativamente mais complexo de testar ( por conta de nem sempre ser possivel prever a ordem das threads, por exemplo ) porem vc só precisa garantir que uma dada quantidade de dados foi processada e com sucesso ( ou seja vc não esqueceu ninguem ).

Pra mim o seu modelo deveria justamente trabalhar com estas questões relevantes. quanto tempo vc vai levar pra computar uma folha de pagamento? vc pode computar em paralelo? como a performance do banco de dados influencia? como vc gera o relatorio? pode gerar o relatorio parcial? como vc monitora que esta indo tudo bem? como vc grava o log? vc espera calcular isso em um dia? qual o maximo? quantos usuarios? pode usar cloud computing? vc pode/deve marcar um dado usuario como urgente e processar ele primeiro? etc.

percebe que algumas perguntas podem não fazer sentido, não conheço o problema. mas se performance não é um problema então foco em calcular correto pq com o salario das pessoas não se brinca.

Esse foi o maior motivo de deu abrir o tópico aqui no GUJ, não quero trabalhar sem essa abstração, pois o sistema de folha em que trabalho hoje, eu por vezes considero o código extremamente bagunçado. Mas como estou na empresa já tem um tempo (e o framework do sistema foi desenvolvido internamente no inicio dos anos 2000, praticamente não existem padrões de projeto, apenas OO mesmo), ainda estou com um pouco de dificuldade para algumas coisas que eu sei que eu deveria saber…rsrsrs

Com relação a folha de pagamento, dei como exemplo por ser o mercado que trabalho a 6 anos, e por realmente ter bastante INSERT, SELEC e UPDATE durante um processamento, e este que normalmente fica em execução por volta de 8 horas em uma empresa com uns 35 mil funcionários. Mas o meu projeto atuará na área da saúde, com algumas situações muito parecidas com o que ocorre na Folha.

Com relação ao controle de transações por meio da DAO, deixaria a própria DAO recuperar uma session do Hibernate?

Faria sentido eu criar uma DAO genérica onde ela saiba fazer o DML básico, e quando instanciar ela dentro de um processo, eu passar os comandos que eu quero que ela execute, estes armazenados em um arquivo .properties? OU o melhor seria fazer uma DAO para cada tipo de processo que possa existir, no exemplo de folha, uma para cálculo de férias, outra para rescisão contrataual e por ai vai.

Eu sei que algumas perguntas são de temas triviais, mas realmente ainda está um pouco nebuloso para mim.

nada impede que vc tenha um DAO generico ( abstract ) e DAO especificos.

porem se vc tem muita logica, vc não vai construir DAO para cada tipo de processo. vc provavelmente vai criar abstrações que sabem calcular cada tipo de processo e estes vão usar os DAOs.

vc pode dar o nome de Serviço se fizer sentido. este Serviço vai saber ler o que fazer de algum lugar ( que pode ser um .properties, um json, um javascript, etc ).

existem varios patterns pra fazer isso.

Seria algo parecido com isso:

Tenho uma DAO que sabe fazer o DML (Independente de qual texto tenha o comando), na minha classe de processo digo: DAO inicie a transação, execute esse comando com estes parâmetros, se deu tudo certo faz o commit.

Seria algo como se as DAO’s fossem um objeto Query.

Claro que isso de uma maneira bem hipotética.

Os processos do sistema que você se refere podem ficar nas classes de negócio, cada classe será responsável por uma funcionalidade específica. Na minha opinião não precisa se complicar com uma arquitetura mirabolante, importante é ser organizado e a equipe coesa. Outra coisa importante é não fazer um “sistemão”, mas dividir em módulos para cada grupo de usuário. Se a aplicação usa banco de dados relacional, então escrever SQL vai fazer parte da codificação da regra de negócio. Mecanismo genérico só vai te engessar para esses “processos” do sistema.

não é só configurar a unidade de persistência no xml e pronto?

Eu achava que hibernate e afins era usado justamente pra evitar que o desenvolvedor da aplicação de folha de pagamentos precisasse tratar qualquer coisa relacionado à acesso ao banco no código.

mas eu tb não sabia que era comum usar DAO com ORM, pra mim não faz muito sentido. :confused:

Talvez eu esteja apenas por fora do mundo Java mesmo!

Você sequer precisa de um objeto na maioria das vezes. Calcular a folha de pagamento nada mais é do que uma função (ou procedure) do estado atual do banco.

Calcular os valores a serem pagos/descontados e emissão de relatórios não é processo, e sim percepção. Todo o resto é processo porque causam alguma mudança no sistema, mas são processos que não precisam ser coordenados entre si pois são sempre executados após uma nova gravação na tabela de resultados, e portanto não precisam fazer parte da transação.

Mas é apenas complexidade desnecessária vazando da camada de infraestrutura né, não tem nada a ver com o negócio?

Realmente alguns não precisam fazer parte de transação, mas a minha dúvida maior é como tratar o isolamento da camada de acesso ao banco, pois como eu citei mais acima, não consigo entender como utilizar o padrão DAO nestes casos.

Quando eu estava na faculdade sempre foram mostrados exemplos do padrão DAO com cadastros simples, e realmente fica muito simples de visualizar, mas nunca encontrei um exemplo (mesmo na faculdade) do padrão sendo aplicado em outros casos, tais como apuração de valores (pois realizam selects), importação de arquivos (que realizam selects e inserts), e nestas ações não serias feitos apenas selects que representam um objeto do modelo.

Não entendi o que você quis dizer, mas falei sobre complexidade técnica desnecessária. Percebo que a geração atual se preocupa mais com siglas do que atender o negócio.

Já que vc falou que precisa manter historico, de uma olhada em Datomic.

Sim, muitos padrões OO só funcionam em situações simples e exemplos de faculdade mesmo.

Frameworks como Hibernate, tb tem suas limitações em sistemas reais.

Por exemplo, você não pode criar queries mais complexas a partir de queries mais simples. Como já foi falado, vc pode customizar, mas não compor. Composição é uma das abstrações mais poderosas (imagina não poder criar uma lista a partir de outra lista), mas um desenvolvedor Hibernate não pode compor queries. :astonished:

Outro problema dessas ferramentas é a performance. O cache local, que em tese, deveria evitar idas ao banco, não é muito útil com dados mutáveis. Se as informações do banco mudam com frequência, o cache vai estar desatualizado na maioria das vezes, e vc acaba tendo que ir ao banco de qualquer maneira.

Datomic vc pode compor queries (queries são estruturas de dados e não strings) então não tem esse problema. E como em Datomic vc trabalha com dados imutáveis, o cache local funciona que é uma beleza. :slight_smile:

Estou dizendo que idealmente, as classes do pacote negócio deveriam conter aspectos do negócio (folha, ferias, rescisão), e não mapeamento O/R e DAOs.

1 curtida

Com certeza.

Vou dar uma olhada sim.

Acredito que isso seja um pouco da resposta da minha dúvida inicial, em dado momento não vou escapar de criar uma classe que acabe mesmo que indiretamente chamando a execução de um comando mais elaborado.

Saindo um pouco do tema de acesso a banco.

Para framework, estava pensando em usar o Spring (MVC), andei lendo um pouco e ele me agradou, não me pareceu tirar performance do sistema, consigo além de retornar as páginas, também consigo retornar um JSON (ótimo para disponibilizar um acesso para outro sistema por meio de REST), e me permite usar o Angular ou o Ext Js na camada de front-end (o Ext foi um pensamento apenas porque o utilizo diariamente na empresa).

Acabei caindo para o lado do Spring devido as opiniões dadas em diversos tópicos na internet.

Mas não custando perguntar, Soring seria uma boa?

Por mais que seja uma ferramenta engessada para “sistemas reais”, a maioria na comunidade Java e até mesmo .NET infelizmente seguem a doutrina que ORM deva ser usado para seguir padrões OO e de forma mais uniformizada, não importando se o cara vai gastar um dia inteiro tirando leite de pedra da ferramenta para gerar algo fora do trivial, que seria muito mais prático de escrever diretamente via SQL no caso de banco relacional. Fora que Hibernate é um peso inútil para aplicações HTTP, onde recursos como Lazy não trazem nenhum benefício para atender a funcionalidade, pelo contrário, só degradam performance com n+1 querys. E cache no servidor web é algo bem questionável, para client desktop ok. Multibanco é uma das poucas utilidades que o Hibernate proporciona, claro que quando isso for realmente necessário.

Muitas empresas usam Java e .NET mas todo o negócio mesmo roda no banco de dados.

Como somente o resultado é disponibilizado para aplicações escritas nessas linguagens, pra quem está de fora, fica parecendo que todo o sistema é em Java ou .NET, mas na verdade é só o CRUD. :slight_smile:

Sendo sistemas internos, sem muito acesso, hibernate parece ser um bom compromisso.

Você pode usar tanto servidor de aplicação, spring ou até mesmo um fat JAR com todas as dependências incluídas pra ser executado diretamente com o comando Java -jar sistema.jar.