Quando uma classe ou módulo deve estar em um Assembly / DLL separado?


22

Existem diretrizes para decidir quando uma classe deve estar em seu próprio assembly / DLL? Muitas vezes vejo duas escolas de pensamento:

1) Todo "agrupamento" de classes pertence à sua própria DLL, por exemplo, Repositórios, Serviços, DTOs, Infraestrutura, etc.

2) Tudo deve estar em uma única DLL, mas separado por namespaces / pastas, por exemplo, ter uma DLL "Core" com namespaces adicionais, como Core.Repositories, Core.Services, Core.DTO, etc.

No trabalho, apenas agrupamos tudo em uma única assembléia chamada "Negócios". Existem algumas pastas, mas não há uma separação real - os objetos de negócios (com lógica, alguns dos quais nem deveriam ser classes) são agrupados em uma pasta "BusinessObjects" sem cuidado. Os itens usados ​​em mais de uma classe estão em uma pasta "Principal". Os utilitários estão em uma pasta "Utilitários", a infraestrutura de acesso a dados é uma pasta "Dados" - você entendeu.

Para um novo módulo em que estou trabalhando, quero / preciso ter uma camada de acesso a dados separada (pense em uma implementação rudimentar do Repositório), mas não quero jogá-la na pasta "BusinessObjects" com as outras 160 (!) aulas lá. Ao mesmo tempo, estou preocupado em criar uma nova biblioteca de classes, pois todo mundo está acostumado a encher uma classe na única biblioteca; uma pasta / espaço para nome pode funcionar no entanto.

Respostas:


12

Acho que é melhor ter mais projetos (por exemplo, assemblies) com classes divididas por categoria em cada projeto do que um projeto com todas essas classes em namespaces separados. Você deve ter como objetivo que seus projetos sejam reutilizáveis ​​e representem diferentes camadas em um aplicativo. Em seguida, é possível reutilizar esses projetos em aplicativos futuros sem precisar incluir um monte de classes desnecessárias.

Por exemplo, com base no que você mencionou, eu definitivamente teria os seguintes projetos:

  • Testemunho
  • Dados
  • Domínio (ou BusinessObjects)
  • Serviços
  • Utilitários (ou Assistentes)

2
Lamentavelmente, não posso votar na resposta. É exatamente isso que é recomendado em nossa empresa. Como resultado, com seis ou mais projetos, por exemplo, para sites de tamanho médio, você não pode manter uma hierarquia de diretórios baseada em recursos, etc. Como resultado, você acaba com os seis projetos, sendo cada um deles um grande e impossível desastre de navegar na pilha de arquivos. Geralmente, as pessoas que aconselham isso nunca tentaram a estrutura de projetos com base em recursos em projetos relativamente grandes (desculpe pelo julgamento direto, mas lidar com os resultados dessa superposição é uma dor real). A resposta dos jammycakes aconselha a abordagem correta.
21417 alehro

Dividir classes por categoria é o que leva à bagunça em grandes projetos. Divida-os por recurso / aspecto. Os namespaces estarão apenas refletindo essa divisão. E toda a estrutura espelha o léxico da especificação. Dividir classes por categoria é como adicionar abreviações de tipo a nomes de variáveis ​​- geralmente é mau cheiro.
21417 alehro

É fácil abusar das duas soluções propostas (ou seja, muitos projetos ou muito poucos). Encontrar um bom equilíbrio em que a reutilização e o baixo acoplamento sejam mantidos em mente deve levar a algo mais testável e sustentável.
Bernard

16

"Tio Bob" Martin, do Clean Code, a fama dos Princípios do SOLID destacou três princípios aqui :

  • O Princípio da Equivalência de Reutilização da Versão: O grânulo de reutilização é o grânulo de liberação.
  • O princípio comum de fechamento: as classes que mudam juntas são empacotadas juntas.
  • O princípio de reutilização comum: as classes usadas juntas são empacotadas juntas.

A regra geral é que você deve manter o número de projetos em sua solução o mais baixo possível. Divida-os apenas se for necessário fazê-lo para implementar uma ou mais histórias de usuário específicas ou se um único assembly estiver causando problemas de desempenho mensuráveis ​​(normalmente quando eles atingem vários megabytes de tamanho).


2
+1 esses princípios são boas diretrizes. A separação de classes em diferentes montagens introduz considerações adicionais sobre o design (por exemplo, quais versões de cada montagem podem trabalhar juntas) aumentando a base de código geral, aumentando o custo de manutenção.
Ben

1
No entanto, além desses princípios, eu acrescentaria que geralmente é uma boa idéia empacotar o código que provavelmente será substituído ou trocado em um assembly separado. Esse código se beneficiará das considerações adicionais necessárias para o acoplamento de conjuntos separados e a sua separação facilitará o teste e a comparação das diferentes versões.
Ben

4
Sim, mas verifique se há um requisito genuíno para que seja substituído ou trocado. Por exemplo, estruturas e bibliotecas de terceiros - pacotes NuGet e similares - geralmente precisam oferecer suporte a vários contêineres IOC e várias estruturas de log. Por outro lado, para o código da terra do usuário, essas abstrações geralmente são puramente especulativas, desnecessárias e obstrutivas, e nunca funcionam no final nas raras ocasiões em que são realmente necessárias.
Jammycakes # 4/14

"Divida-os apenas se você precisar fazer isso para implementar uma ou mais histórias de usuários específicas ou se uma única montagem estiver causando problemas mensuráveis ​​de desempenho (normalmente quando eles atingem vários megabytes de tamanho)." - totalmente aleatória e não fundamentada em aconselhamento realidade
hyankov

1
Sinto muito, mas exatamente o que é "totalmente aleatório e não fundamentado na realidade" sobre isso? Postei essa resposta depois de anos trabalhando em soluções que foram divididas em muito mais projetos do que o necessário, por nenhuma outra razão além de É assim que você deve fazê-lo. O resultado? Tempos de compilação glacial e um pântano do inferno da dependência (especialmente nos dias anteriores ao NuGet). Dividir as coisas em montagens separadas tem um custo, e se não houver nenhum benefício para compensar esse custo, é apenas um caso de roubo dos negócios.
jammycakes

4

Outros princípios orientadores com os quais trabalho:

  • Você acha que reutilizará esse código em outros projetos? Para um grupo de aplicativos Web relacionados, tínhamos um módulo relacionado à conta do usuário que todos os aplicativos utilizavam, pois todos usavam o mesmo modelo para contas e logins do usuário. Fiz coisas semelhantes com bibliotecas de geometria e matemática e as reutilizei em vários aplicativos, incluindo a DLL.

  • Deseja modificar / implantar esse código sem reimplementar / recompilar todo o projeto? Às vezes, é útil apenas reconstruir o módulo, implantar e reiniciar o aplicativo Web.

Parece que, no seu caso, um Repositório básico e genérico pode ser útil novamente no futuro; pode valer a pena separá-lo em uma nova DLL, se você puder.


Eu acho que o seu segundo ponto é verdadeiramente razoável. A divisão em módulos deve ajudar no tempo de desenvolvimento / teste e compilação.
WM
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.