Estou com uma grande dificuldade em implementar uma arquitetura básica para meu sistema.
O problema é conceitual. Hoje tenho um sistema em desenvolvimento onde estou usando um modelo que já foi criticado muito aqui e em outros fóruns que é a separação entre o estado e o comportamento onde eu tenho uma classe Usuario com as regras de negocio UsuarioBLL e outra com os gets e sets (Classe burra) Usuario.
Sei que essa abordagem não é legal por n motivos, acho que o correto seria a junção dessas duas classes em uma única - Usuario com estado e comportamento, entretanto eu não sei como usar esse novo objeto, não tenho o conceito correto das coisas.
Por exemplo eu tenho uma classe usuário (completa, com estado e comportamento) e preciso fazer algumas operações triviais, como autenticacao, inclusao, alteracao e consulta.
Todos esse métodos (addUser, alterUser e GetUser) estariam na classe usuário? E se eu precisar pegar todos os usuário que fazem aniversário em uma data específica, também usaria esta mesma classe?
Você pode colocar seus métodos de busca em uma classe de domínio chamada Repositorio.
Não tem nenhum problema passar objetos do seu domain model entre as camadas do sistema. Isso pode ser um problema ao passar os objetos em tiers, mas não entre camadas.
não sei se eu entendi bem,mas seu usuário sabe se adicionar alterar no banco? (addUser, alterUser )
se sim não sei se isso é correto pois isso implica em ele ter conhecimento de conexão com o banco e ao meu ver isso é uma função de outro objeto… mas espero que outra brother possa responder essa minha duvida q pode ser sua tbm se entendeu DDD…
[quote=baudamix]não sei se eu entendi bem,mas seu usuário sabe se adicionar alterar no banco? (addUser, alterUser )
se sim não sei se isso é correto pois isso implica em ele ter conhecimento de conexão com o banco e ao meu ver isso é uma função de outro objeto… mas espero que outra brother possa responder essa minha duvida q pode ser sua tbm se entendeu DDD…
[/quote]
Vamos esquecer banco. De uma maneira ou de outra, ele terá… veja o porquê :
Como o amigo falou… agora vc esquece os DAOS e pensa em repositórios…
[code]class Aluno {
//atributos, gets e sets
public void save() {
repositorio.persist(this);
}
}[/code]
ok…
peerless, então o usuario terá que conhecer o objeto que o persiste?
class Usuario {
//atributos, gets e sets
//como sitei o objeto vai ter que de alguma maneira conhecer o mecanismo de percistencia?
Repositorio repositorio = new Repositorio();
public void save() {
repositorio.persist(this);
}
}
[code]class Usuario {
// ou uma pouco mais inteligente patter(good citizen)
Repositorio repositorio;
public Usuario(Repositorio repositorio) {
this.repositorio = repositorio
}
//atributos, gets e sets
public void save() {
repositorio.persist(this);
}
As pessoas falaram aqui sobre Repositório, que é um conceito de Domain-Driven Design. Existem ons livros sobre isso, um com exemplos e explicações em C# inclusive.
Seja usando um Repository ou diretamente um DAO (assumindo que você não usa Domain-Driven Design) você pode deixar a operação de salvar a obtêr dados com ele. Pense no DAO como algo que salva o estado atual no banco de dados, ele não executa nenhuma reras, apenas tira uma foto da memória e joga no banco -e vice-versa.
Nota: Não existe problema no objeto se salvar se você está usando algo como o padrão Active Record. Dê uma olhada em como isso é feito no framework Castle. Mas no geral você vai usar um Data Mapper, que transfere a responsabilidade para outro objeto e é a coisa mais simples, creio. AR é útil em casos específicos.
Sim, conhece! Como o calcado falou. Repository é uma abstração e faz parte do negócio, não há problema! PS.: Isso CONSIDERANDO Domain Drive Design, se é isso que você almeja!
Meu nível e conhecimento das terminologias usadas está bem aquem dos caros participantes e tenho dificuldade de entender alguns conceitos, de qualquer forma gostaria de mostrar a forma como eu faço e saber de vocês alternativas a esse meu modelo e como resolver certos problemas.
Vou tomar a classe usuário como exemplo, sem um contexto pré-definido.
A intensão é ter uma abordagem o mais simples possível, porém que eu possa progredir aos poucos com meu aprendizado.
Cenário A - Incluir Usuario
Meu processo é o seguinte: Ao clicar em um botão incluir usuário na inteface eu instancio um objeto usuário e o populo com os dados do form:
Usuario objUser = new Usuario();
objUser.Nome = nome no formulario;
…
objUser.Incluir();
No meu objeto de negócio Usuario, tenho um método incluir que chama um DAO passando o objUsuario que faz a inclusão no banco. 1) Isso é Active Record ?? Na minha modelagem o DAO sempre faz o que o pcalado comentou, apenas grava, altera e recupera informacoes do banco, sem qualquer lógica de negócio.
Cenário B - Recuperar 1 usuário e seus cursos realizados por exemplo, mostrando essas informações na tela.
No load da interface ou em algum outro evento chamo minha camada de negócio, qual? usuário? cursos? outra camada de serviços? Neste caso preciso ter desempenho e recuperar as informações do banco de uma só vez através de um join. Como transferir entre as camadas estas informações até a interface?
Gostaria também de um exemplo prático de como usar o Repository, o conceito eu entendi, explicado em outro post, mais um exemplo prático viria bem a calhar.
Eu acho que entendi sua dificuldade amigo.
Está mais relacionado à questão de boas práticas e facilidade de manutenção posterior, veja bem, esta é a minha opinião e os outros colegas podem me corrigir.
Vc pode utilizar uma estrutura semelhante a isto:
entidade de domain bean.Usuario
camada de regras vo.Usuario
addUser(Usuario u)
removeUser(Usuario u)
comunicação com o banco de dados. INSERT, UPDATE, SELECT
Aqui é acessado pelo VO que logicamente trabalha com os Beans (populando ou explodindo) ejb.dao.Usuario
salvar(Usuario u)
apagar(Usuario u)
Usuario[] obter()
camada de persistência, regras etc, sem comunicação com o BD ejb.session.Usuario
Até certo ponto arquitetura não depende da linguagem. Depende mais da plataforma , mas no caso nem existe essa dependencia.
Depende do que vc quer fazer. Adicionar save() no seu user não é legal. Adicionar autorize() no seu user não é legal. Adicionar getUsername() é .
Se é trivial é porque é muito comum. Se é muito comum não é exclusivo de “usuário”. Vc deve tratar tudo da mesma forma. Autenticação é algo do dominio especifico de usuário. Isso já não é comum e deve ser feito à parte
Vc precisa de :
PersistanceManager : Classe que se encarrega de inserir , alterar e remover seu objeto do banco.
Ela se encarrega também de executar queries genéricas e retornar os dados.
AutenticationService : Classe que se encarrega de autenticar o usuário. O usuário não se pode autenticar a si mesmo. Por definição autenticação é algo externo a ele. Ele só tem que apresentar as credenciais ( normalmente nome e password).
Usuario: Classe que mantém o estado para cada usuário.
Eliminar o TO não significa despejar todas as funcionalidades numa única classe.
O que é necessário é uma boa separação de responsabilidade. É quando a separação é maior que deveria
que acontece o problema dos VO-BO. Isso não significa que não existam classes que manipulam outras.
No caso do usuário o exemplo de BO seria obter as permisões do usuário.
ERRADO
UsuarioBO bo = ...
List<Permisison> permisisons = bo.getPermissionsFor(usuario)
// ---
if (bo.userHasPermissions(user,permission){
}
CERTO
List<Permisison> permisisons = usuario.getPermissions();
// --
if (usuario.hasPermission(permission)){
}
Não ha porque tirar a responsabilidade do usuário de conhecer suas permissões. Isso não é natural.
Detalhes de implementação como trabalhar com persistencia devem ser incluidos em classes que fazem apenas isso e fazem bem. Imagine assim : quando vc tira uma foto para o RG vc não tem que saber tirar a foto e vc não tem responsabilidade sobre o fabrico do RG. Contudo o RG é seu. Quando alguem quer autenticar vc , irá pedir seu RG.
O fotografo é o cara que persiste sua informação. Vc não precisa saber fotografia para que sua cara fique grava para a posteridade. O usuário tb não precisa saber como se salvar no banco.
A suas credenciais são lhe dadas por outro ( AutenticationManager ) e sua autirização de entrar em algum lugar é veriicada por um terceiro (AutorizationManager) vc é só um portador das credenciais.
Veja por outro lado: Mecanismo de autenticação mudam, mas vc não. Se a responsabilidade de autenticar for do usuário ela não poderá mudar sem alterar a classe usuário. Isso significa que ha um acoplamento. E isso é ruim.
Espero que tenha ficado mais claro, quando e quando não vc deve criar objetos especiais que manipulam seu objeto principal. Em geral vc tem que fazer isso se quiser que seu sistema seja desacoplado. O problema é quando vc desacopla demais.
Sim. O objeto que vai ser persistido (record) é ativo na sua persistencia (active) porque sabe como levar a cabo essa operação.
Isso é bom ou ruim ?
Em geral é ruim. Por vários motivos. O mais importantes são:
O usuário tem responsabilidades demais. Isso viola o principio de separação de responsabildiade.
Se existe um DAO o usuário apenas delega. Isso significa que o usuário chama para si uma funcionalidade que não é sua. Se fosse, ele não delegaria.
O ActiveRecord só é viável se existem muito poucas entidades e os objetos que as representam acessam diretamente a API do banco.( no java é JDBC , no C# é o ADO)
Estou-lhe a dizer para mudar ? Não. Vc faz o que vc quiser. Estou apenas lhe dizendo que do ponto de vista teorico o que vc está fazendo viola principios básicas de OO.
o DAO é o objeto que comunica com a API do ADO ( na realidade DAO e ADO significam a mesma coisa, mas aqui ADO é uma tecnologia da plataforma .net)
Ele usa SQL para fazer os queries. O SQL depende da entidade (tabela) que está sendo consultada.
Em caso especiais é necessário fazer joins etc… O DAO não deve conter essa informação porque é especifica do
dominio. Para resolver esse problema entra o repositório. O Repositorio é um padrão genérico que abstrar a API de consulta. Vc pede ao repositorio que encontre algo e ele criará a SQL para isso. Enviará ao DAO e retornará.
Entenda que o Repositorio serve para esconder a criação do SQL, não para a executar. Isso é trabalho do DAO.
Se no futuro o SQL necessário para responder aquelas perguntas mudar, vc só muda o que está escrito no repositorio e não na aplicação como um todo. Ou seja, não tem que andar garimpando onde está escrito o SQL.
Agora vc pergunta: “Mas o correto não seria : ?”
List<Curso> cursos = usuario.getCursos(date);
Não é que é mais correto, é que é mais prático. Representa melhor a relação entre usuario e seus cursos.
Esta é a forma perferida se puder ser usada.
Por dentro de usuario teriamos
public List<Curso> getCursos(Date date){
return rep.cursoRealidadosAte(this, date);
}
Peciso usar o repositorio aqui ? Não poderia criar o SQL e invocar o DAO ?
Poderia. Mas não deve. Assim vc tem a criação de SQL localizada. isso é bom para a manutenção.
“Onde é que está mesmo o SQL que procura os cursos … ?” Se usar repositorio a resposta é obvia.
Entenda que ha uma diferença entre as operações de inserção/alteração/remoção e o query.
O query é passivo. ele não muda o estado do sistema. Fazer o usuário delegar ao repositorio é um mecanismo passivo de separação de responsabilidade. Fazer o usuario delegar ao DAO é um mecanismo ativo de aglutinação de responsabilidade. Ha uma diferença intrinseca e importante.
Nota: Usuario é um papel criado pelo sistema de autenticação. usuários não pertencem ao dominio.
O que vc deveria ter era a classe Aluno. Ela está relacionada aos cursos. O usuário está relacionado ao aluno em 1:1 mas não são o mesmo objeto. Até porque, podem existir usuários que são Professores ou Diretores, etc…
lusilva1982
Se foi o que eu entendi era assim que eu fazia, mas me aconselharam a não usar VOs, DTOs para trasferir os objetos de uma camada para outra, apenas se eu precisar passar entre Tiers por uma questão de performance.
sergiotaborda
Obrigado por seus comentários, entendi que o objeto faz somente o que é de seu conhecimento, certo? Ela não sabe se incluir, e também não sabe se autenticar, mais sabe calcular a idade por exemplo. Entendi também que é interessante eu ter uma classe que faz a autenticação, legal, mais no caso da inclusão, quem seria responsável por incluir um usuário, ou melhor incluir um aluno? Você sugere que seja uma classe PersistanceManager ela faria todas as inclusões, alteracoes e remoçoes (aluno, curso etc) ou eu teria PersistenceManagerAluno, PersistenseManagerCurso etc?
[quote]Estou-lhe a dizer para mudar ? Não. Vc faz o que vc quiser. Estou apenas lhe dizendo que do ponto de vista teorico o que vc está fazendo viola principios básicas de OO.
[/quote]
Mas Sergio, o que eu quero é justamente mudar, pra melhor claro, e é por isso que estou procurando a ajuda de vocês. Senão deixava tudo como está.
public List<Curso> getCursos(Date date){
return rep.cursoRealidadosAte(this, date);
}
Sobre o repository e sobre consultas especializadas como eu faria na prática? Meu repositório seria algo geral ou eu teria RepAluno, RepCursos etc? Outro detalhe onde eu monto o objeto de retorno.
Seria algo assim?
public class Repository ou RepositoryAluno?
{
public List<Cursos> cursoRealidadosAte(Usuario user, DateTime data)
{
sql = "select.....";
resultado = DAO.ExecutaSql(sql);
foreach resultado
{
monto os cursos
}
retorno os cursos
}
}
Obrigado a todos. Estou progredindo bastante com esta discussão. Vlw.
Sim. (Não necessáriamente do seu conhecimento. Da sua responsabilidade. Uma criança de 1 ano não sabe matemática - conhecimento - mas tem responsabilidade de responder a “quantos anos tem?” )
Ai é um pouco de tecnica e menos de modelagem.
Se vc conseguir usa o padrão DomainStore: ou seja, um só objeto é responsavel por persistir todos os objetos do dominio, otimo. É mais complexo. Em java com o uso de reflection e metadados é simples. Não sei se em .net é tão simples. Mas vc tem o NHibernate que lhe pode dar uma ajuda.
Se vc usar uma classe para cada um , ok tb. O que não pode acontecer é vc ter uma classe para cada um que delega para uma só classe.
O mesmo que os persistence manager. Se vc quiser usar um só, ok. Se quiser usar vários tb.
Ai é um pouco mais de escolha conforme o que é mais facil manter. O que é mais facil ensinar para quem vai mexer no sistema e o que é mais facil lembrar daqui a anos.
O ruim de criar um para cada um é a dispersão , mas mantém as regras “em pacotes”. O bom de por tudo num é que está tudo num só lugar, mas isso e´ruim porque tem que procurar muito pelo que precisa.
Então são duas situações distintas: Uma quando quero alterar/incluir/remover meu objeto (mudança de estado) e outra quando faço consultas (não mudo o estado do objeto). Isso determina uma abordagem diferente?
Então é aconselhável delegar responsabilidades
class Aluno
{
public List<Curso> getCursos(Date date)
{
return repositori.cursoRealidadosAte(this, date);
}
}
Mas não delegar mudança de estado
[code]class Aluno {
//atributos, gets e sets
public void save() {
repositorio.persist(this);
}
} [/code]
Como seria o processo quando preciso fazer mudanças de estado? Como eu faria a inclusão do usuario por exemplo? Chamando direto o repositório? Não entendi poderia dar um exemplo?
Sim.
Por exemplo alterar o estado precisa estar numa transação. Ler não precisa ( embora possa estar).
Ler muitos dados é complexo. É preciso otimizações como o padrão Flyweight ou o FastLane.
O primeiro minimiza os dados no objeto ( corresponde ao select de apenas alguns campos) o segundo
deixa não cria o objeto final até que ele é chamado e ai ele lê diretamente do resultado do DAO.
Estratégias diferentes só são poissiveis de houver uma abordagem diferente.
A alteração de estado é sempre parte de um processo. O processo é normalmente transacional.
Então vc teria alguma classe/método que inicializa esse processo dentro de uma transação e chama o DAO
para gravar/alterar o objeto. Lembre-se que em um só processo vc pode necessitar alterar vários objetos ,até de classes diferentes.
Um exemplo:
public void onSaveClick(){
Conta conta= new Conta();
// Lê dados da tela para a entidade
Lancamento lancamento = new Lancamento(0, now() , conta);
Transaction t = ... // a transação norlamente é automaticamente injetada. aqui é só para ficar claro
try{
t.begin()
dao.insert(conta);
dao.insert(lancamento); // iniciliza o saldo a zero fazendo um lançamento de zero.
t.commit();
} catch (Exception e){
t.roolback();
}
}
O DAO está incluindo a Conta e Lancamentos, mais o meu dao é burro ele não sabe dar insert nem conhece os objetos conta e lancamento. Não seria repositório que montaria minha instruçao SQL e passaria só a execução ao DAO?
O DAO está incluindo a Conta e Lancamentos, mais o meu dao é burro ele não sabe dar insert nem conhece os objetos conta e lancamento. Não seria repositório que montaria minha instruçao SQL e passaria só a execução ao DAO?
[/quote]
Sim. Se seu DAO é burro vc precisa passar pelo Repositorio para mondar o SQL.
Acho que a questão nem era essa, mas sim o fato de não passar pela classe de Usuario, chamar diretamente a classe de persistencia (DAO ou Repository). Né? Entendi.
Pô vlw, consegui entender bastante coisa, finalmente. Estava muito confuso.
Agora vou mexer no meu sistema que tá todo com DTO e uma mistureba danada de responsabilidades. Vou refatorá-lo e com certeza ficará bem melhor. Depois com o tempo eu vou aplicando novos padrões, se for o caso, e melhorando aos poucos.
Novas dificuldades e trapalhadas eu volto a pertubar vocês.