Array
(
)

Design, Arquitetura, Problema com requisitos e acoplamento

Vitor Rubio
   - 28 set 2012

Estou fazendo uma aplicação .net usando nHibernate para a persistência, mas criando repositórios, assim o uso do hibernate é transparente para as classes que usam o repositório.
No meu código eu separei as classes de negócio, de repositório, de aplicação e de apresentação (não está muito bem separado, mas estou aprendendo)
Tenho uma classe de negócio de Log, que usa um repositório de log para gravar no banco de dados um log de qualquer operação CRUD.
Como qualquer operação CRUD é logada, coloquei o "uso" ou chamada dessa classe de log na base do repositório. No entanto não me parece o melhor lugar para colocar isso, por dois motivos:
1) Eu preciso logar o usuário que fez as alterações CRUD, então eu passo uma instância de um objeto usuario para obter seu nome ou id.
2) Eu preciso de informações do browser, ip, informações da sessão e assim por diante, por isso insiro um objeto Sessao, que contém internamente um HTTPContext
Com isso eu crio um forte acoplamento do repositório com as classes sessão e usuário, e com o HTTPContext. Isso me impede de usar minhas classes de negócio e de repositório em uma aplicação windowsforms.
Sei que essa não é a melhor forma de fazer isso, gostaria de sugestões de melhoria. Não sei se devo retirar o log da camada de repositório e passar para a camada de aplicação ou se deveria usar injeção de dependência. Qum poderia me dar um exemplo de DI?
Abaixo uma amostra do meu código
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using LiderTel.Modelo;
namespace LiderTel.Persistencia
{
public class BaseRepositorio<T> where T: Entity
{
private Sessao _sessaoInjetadaRepositorio;
private Usuario _usuarIoinjetadoRepositorio;
protected virtual Sessao GetSessaoInjetadaRepositorio()
{
return _sessaoInjetadaRepositorio;
}
protected virtual Usuario GetUsuarioInjetadoRepositorio()
{
return _usuarIoinjetadoRepositorio;
}
public BaseRepositorio(Sessao sess, Usuario usu)
{
_sessaoInjetadaRepositorio = sess;
_usuarIoinjetadoRepositorio = usu;
}
public BaseRepositorio(Sessao sess)
: this(sess, sess==null?null:sess.UsuarioCriador)
{
}
public BaseRepositorio()
: this(SessionServices.GetSessaoCorrente(), SessionServices.GetSessaoCorrente()==null?null:SessionServices.GetSessaoCorrente().UsuarioCriador)
{
}

private string RetornaIdUsuario()
{
return (GetUsuarioInjetadoRepositorio() != null) ? GetUsuarioInjetadoRepositorio().ID.ToString() : "";
}
public virtual void Add(T ent)
{
ent.Criacao = DateTime.Now;
ent.UsuarioCriador = GetUsuarioInjetadoRepositorio();
ent.Excluido = false;
ent.Ativo = true;
NHibernateHelper.GetDefaultSession().Save(ent);

Log log = new Log();
log.Acao = string.Format("{0} {1} criado por {2}.", ent.GetType().Name, ent.ID.ToString(), RetornaIdUsuario());
log.TipoEvento = TipoEventoLog.Inclusao;
log.Entidade = ent.ID;
if (GetSessaoInjetadaRepositorio() != null)
{
log.SessaoID = GetSessaoInjetadaRepositorio().ID;
}
log.ComoEstava = ent.InternalToString();
LogRepository logrep = new LogRepository(GetSessaoInjetadaRepositorio(), GetUsuarioInjetadoRepositorio());
logrep.Add(log);
}
}
}

Washington Morais
   - 01 out 2012

Olá Vitor.

Uma sugestão seria voce utilizar uma camada de Services que seria responsável pela comunicação entre sua camada UI e a de negócios. Crie um objeto de modelo especifico com as informações que voce precisa e trafegue este objeto entre as camadas através do serviço.

Com isso, é possivel reutilizar os metodos entre diferentes aplicações.

Abraço.

Vitor Rubio
   - 01 out 2012

Já estou criando essa camada. O problema é que, como eu disse, a minha necessidade de log (e armazenar ip do browser, ip do servidor etc) cria um acoplamento entre minhas classes de negócio e a classe HTTPContext do .net, e um acoplamento entre o repositório e a classe de log, o que impossibilita que eu aproveite essa camada de negócios em um ambiente windowsforms.

Washington Morais
   - 02 out 2012

Bom, não sei se estou viajando ou não entendi direito, mas a ideia é justamente criar uma classe de negócio onde estas informações são passadas desde a UI até a classe de LOG.

O fato de voce precisar destas informações especificas do browser, "te obriga" a recuperar estas informações na camada de UI ou utilizar o objeto Session do HttpContext em outras camadas, o que acaba não sendo uma boa pratica.

Uma outra possibilidade é que seu metodo no serviço espere como entrada um XML ( o padrão voce pode definir ) e todos que implementarem sua interface do serviço terão que te enviar este XML com as informações que o seu LOG precisa.

Com isso, qualquer liguagem que faça uso de XML poderá consumir seu serviço, independente de ser uma aplicação Web ou não.

Abraços

___________________
Washington Morais
MCP / MCTS

Vitor Rubio
   - 02 out 2012

Minha aplicação não é baseada em webservices. O meu problema não é bem o acoplamento entre o log e o HTTPContext, porque esse acoplamento eu posso resolver criando uma interface chamada ISessao, e criando duas classes que implementem essa interface: uma SessaoWeb e uma SessaoDesktop, assim eu posso passar um isessao para o log, sendo este ou um SessaoWeb ou um SessaoDesktop, dependendo da plataforma. Posso fazer com que os métodos da interface sejam os métodos que me retornem o usuário, host, etc. Dá para usar o objeto request na web e as classes para lidar com usuário no desktop.

O problema é mais sutil: Todas as classes do meu repositório estão acopladas ou com o Log ou com essa ISessao. Para cada classe do repositório eu tenho um constructor onde eu passo o ISessao. A classe ancestral do meu rep. usa esse objeto passado pelo constructor para fazer os logs de operações CRUD.

Não sei se está certo isso, de passar um isessao para os repositórios, ou se eu deveria tirar de dentro do repositório essa operação de log. O que você acha?

Washington Morais
   - 02 out 2012

Por padrão, quanto menos acoplado melhor :).

A idéia de utilizar WCF é justamente para que as regras de sua aplicação possam ser consumidas por qualquer front-end. De qualquer forma, voce ainda pode usar o XML como entrada de dados. Com isso, suas classes não precisam ir trafegando objetos de sessão.

Abraços

___________________
Washington Morais
MCP / MCTS

Vitor Rubio
   - 02 out 2012

Ok, esqueça o objeto Session do ,net framework. Qaundo eu disse Sessão estava me referindo a minha classe de sessão, que é algo mais ou menos assim:

interface Isessao
{
Usuario GetUsuario();
string GetNomeComputador();
string GetIP();
}

suponha que eu faça via WCF ou SOAP webservices. A questão não é acoplamento entre UI e camada de aplicação, ou acoplamento entre camada de aplicação e objetos de negócio.

O problema é mais abaixo: eu acoplei os repositórios com o Log, pois todas as operações no repositório são logadas. Como efeito colateral, como log precisa de um isessao, eu tenho que passar o Isessao no construtor do repositorio. ou seja: repositorio -->depende de log --> depende de isessao.

Como reduzo essa dependência? É correto usar o log dentro do repositório ou eu teria que criar mais uma camada?