Objetos de negócios em uma camada de acesso a dados

12

Então, eu tenho criado uma camada de acesso a dados via TDD e me aproximei de alguma preocupação. Prefiro não seguir o caminho errado, então imaginei pedir a vocês para ver se meus pensamentos estavam alinhados com uma arquitetura limpa.

Os métodos dentro da minha camada de acesso a dados (DAL) são bastante simples. Eles estão alinhados com os procedimentos armazenados no banco de dados (não há outra maneira de entrar nele para manter as coisas limpas) e contêm os mesmos parâmetros que os procedimentos. Eles então se conectam ao banco de dados e retornam o resultado da consulta. Aqui está um exemplo:

public int DeleteRecord(int recordId)
{
    recordId.RequireThat("recordId").NotZeroOrLess();

    List<SqlParameter> parameters = new List<SqlParameter>();
    parameters.Add(new SqlParameter { ParameterName = "@RecordId", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Input, Value = recordId});

    return this.ExecuteNonQuery("DeleteRecord", parameters.ToArray());
}

Isso funciona perfeitamente para esse tipo de método porque não estou fazendo nada significativo com o conjunto de resultados. Eu só quero ter certeza de que o comando funcionou, então retornarei o resultado da não consulta, que é apenas as linhas afetadas, e posso verificar a lógica usando esse número.

No entanto, digamos que em outro método DAL, desejo carregar um registro. Meu procedimento de carregamento será executado selectsem várias tabelas e retornará a DataSet, mas estou discutindo se meu DAL deve criar os Objetos de Negócios dentro do método usando o DataSet, ou se meus próprios Objetos de Negócios devem ter apenas um Load()método que obtenha o DataSetdo DAL e, em seguida, se preenche basicamente.

Fazer isso através do DAL resultaria em menos lógica nos Objetos de Negócios (mesmo sendo apenas lógica de seleção, ainda é lógica), mas aglomeraria o DAL um pouco e faria parecer que realmente está fazendo algo que não deveria ' Não estou fazendo.

O que é que vocês acham?


fonte
Por que você não usou o Entity Framework?
Jfrankcarr
@jfrankcarr - Para ser sincero, principalmente porque não estou tão familiarizado com isso quanto deveria. No entanto, eu precisaria refazer minhas tabelas e adicionar as chaves estrangeiras apropriadas etc. para que o Entity Framework reconhecesse os relacionamentos corretamente. Apenas por curiosidade, se eu o estivesse usando, faria toda a seleção usando a estrutura com os próprios Objetos de Negócios ou ainda haveria uma decisão sobre onde colocar essas consultas LINQ?
Eu recomendo reservar um tempo para aprender EF. Pode parecer um pouco assustador no começo, especialmente ao tentar ajustá-lo a um banco de dados existente que tenha alguns problemas de design pré-existentes, mas vale a pena.
Jfrankcarr
Você também pode consultar o NHibernate se desejar procurar outra opção.
01001100
@jfrankcarr - Definitivamente vou investigar, mas como ele se encaixa em um aplicativo de acesso a dados em várias camadas? A estrutura da entidade seria implementada dentro do próprio DAL, ou dentro de outra camada ou até dos próprios Objetos de Negócios?

Respostas:

4

Seu DAL deve retornar seus objetos de dados

Idealmente, o seu DAL deve ser um objeto de "caixa preta", que o código do aplicativo pode usar para solicitar um objeto de dados ou manipular objetos de dados existentes. Às vezes, há outra camada colocada entre o DAL e o código do aplicativo chamado the Repository, que separa ainda mais as duas camadas, embora isso nem sempre seja necessário.

Além disso, você geralmente não deseja que seus objetos de negócios possam se criar. Isso pode causar falhas de segurança nas quais alguém pode usar sua biblioteca e criar uma nova instância do seu objeto chamando .Load(someId)-o, e ele mescla duas camadas que devem ser completamente separadas.

Também não recomendo fornecer um .Load(DataSet ds)método porque, se a definição do conjunto de dados for alterada, você terá que caçar os objetos de dados que usam esse conjunto de dados e alterá-los. É mais fácil manter todo o seu código de acesso a dados em um único local; portanto, se você alterar a consulta de acesso a dados, precisará alterar apenas sua camada DAL.

Rachel
fonte
Não há certeza de como uma camada pode ser dependente para "retornar o objeto correto" se a definição de "objeto correto" for mantida em outra camada.
TMN
@ TMN Isso foi mal formulado. Mudei um pouco a redação porque você está certo, o código do aplicativo deve saber que tipo de objeto está pedindo.
27412 Rachel
@ Rachel - Gotcha. Portanto, você recomendaria que o DAL retornasse uma instância de qualquer que fosse o meu Objeto de Negócios, correto? Fiquei meio confuso com a sua formulação de "objetos de dados", mas acho que entendi. Dessa forma, meu código poderia solicitar um Objeto de Negócios de onde ele precisasse (não através deles mesmos), apenas chamando BusinessObject bo = DAL.LoadRecord(id);- parece certo? A lógica para mapear a consulta para o próprio BO estaria contida no DAL e somente lá.
1
@ Scott Está correto, apesar de eu chamar o método DAL de algo como, em Getvez de Load, comoCustomer c = DAL.GetCustomer(id);
Rachel
2

Meu método, mesmo antes do LINQ-To-SQL e do Entity Framework, era ter uma interface e uma biblioteca de classes abstratas que forneciam um "contrato por escrito" para a comunicação entre as diferentes camadas do aplicativo. Isso às vezes é chamado de ontologia , uma definição para um domínio de trabalho. Qualquer coisa que passasse entre as camadas usava esse 'contrato'.

Não gosto da ideia de passar objetos brutos do conjunto de dados da camada de dados para a camada comercial. Eu já vi esse resultado em vários problemas, especialmente ao integrar fontes de dados herdadas. Também pode tornar muito difícil para as novas pessoas que entram em um projeto entender de onde vêm os dados. Por fim, exige que sua camada de negócios esteja no negócio de manipular dados diretamente do banco de dados, o que pode levar a complicações no futuro.

O código de exemplo que você tinha se parece com o código que eu tinha antes do LINQ. Eu tinha uma classe de função de banco de dados comum que usei dentro dos meus objetos DAL. As classes DAL liam os dados e os encaixavam nos objetos 'contract'. Resultados escalares, como o seu exemplo de exclusão, retornariam um valor, geralmente um valor booleano.

jfrankcarr
fonte
1
"Não gosto da ideia de passar objetos brutos do conjunto de dados da camada de dados para a camada comercial". Este. Mil vezes, isso.
Joshua Smith
@jfrankcarr - Meu DAL realmente implementa uma interface, e eu pretendo ter interfaces para tudo o que transfere dados de uma camada para outra, então acho que nossas idéias de padrões correspondem lá. Então, você recomenda que eu altere os métodos que estão retornando o resultado direto das ExecuteScalarconsultas para retornar valores que fazem mais sentido para uma camada de negócios, como bool? Penso de outra forma, esta é uma resposta muito semelhante à de Rachel.
Normalmente, retorno um booleano para criar / atualizar / excluir chamadas, a menos que eu precise do número de registros afetados. Por exemplo, eu poderia retornar um int se um processo armazenado estiver processando várias linhas de pedidos ou algo assim.
Jfrankcarr
0

Seu DAL deve retornar um conjunto de dados. Esse conjunto de dados retornado deve ser o objeto de negócio, não deve haver nada que você precise fazer além de verificar se possui os dados esperados. Se você precisar fazer mais com ele, tente fazer demais em um único procedimento armazenado ou não retorne os dados corretamente no procedimento armazenado.

Ryathal
fonte
0

Eu recomendo que seus objetos de negócios tenham um construtor para preencher a partir de um conjunto de resultados. Isso remove o acoplamento entre seu DAL e a camada de negócios. Se você deseja isolar completamente os dois, crie um mapa simples dos pares nome da coluna => valor do seu conjunto de resultados e passe-o para o construtor.

TMN
fonte