Por que encontramos software .NET estruturado em vários projetos separados em uma solução?

Marcio Nizzola
6 min readNov 16, 2023
Por que encontramos software .NET estruturado em vários projetos separados em uma solução?

Após anos trabalhando com .Net, é uma pergunta muito comum inclusive em aulas: “Por que vemos algumas soluções com diversos projetos separados ?”

Normalmente não se abordam nos cursos em níveis iniciais o uso de soluções no .Net com múltiplos projetos, talvez para não complicar o entendimento ou simplesmente por que a grade curricular não contempla isso, mas é muito importante saber que isto é muito utilizado e como fazê-lo, pois no mundo real as coisas são diferentes do mundo acadêmico.

Para alguns isso soa como “complexo”, mas no fim das contas, por que se organizam soluções em tantas camadas separadas por projetos distintos? Quem vê exemplos de arquiteturas “modelo” compartilhados por vários experts na área, de início fica um pouco perdido sobre o motivo desta separação, aqui vou dar um exemplo de um projeto com várias camadas que já participei, e tentar explanar o por que desta separação !

Alguns dos motivos:

  • Separação de responsabilidade: Separando as responsabilidades de acesso à dados, comunicação externa, domínio, entre outras, podemos deixar cada parte específica do projeto separada facilitando o entendimento e diminuindo o acoplamento.
  • Reutilização de código: componentes comuns podem ser compartilhados em diversas partes do código, e até podem ser utilizadas por diferentes aplicações.
  • Preparação para escalabilidade — quando separamos o domínio do projeto em partes, criando um projeto para cada parte do domínio, podemos estar preparando um “monolito modular” para uma futura separação em serviços separados.
  • Testabilidade: podemos criar projetos de testes para cada parte da aplicação separadamente, podendo ter testes de unidade e integração isolados dos demais.
  • Colaboração em equipe: podemos ter equipes diferentes trabalhando em projetos separados, mas ao final ter um único produto integrado, onde uma equipe nem precisa saber como a outra desenvolveu o seu projeto, apenas conhecendo a interface pode integrar este projeto ao seu. Isto garante agilidade e facilidade de desenvolvimento de cada parte da aplicação.
  • Facilidade de manutenção: A localização de partes específicas do código fica muito mais fácil, pois deixamos de ter código de alto acoplamento e sabemos onde exatamente está cada parte da aplicação, separadas em projetos distintos.
  • Segurança: a separação em projetos permite aplicar restrições de segurança específicas a diferentes partes do aplicativo. A camada de acesso à dados pode ter sua proteção específica sem se preocupar com as definições da camada de API que terá a sua segurança em outros aspectos.
  • Desenvolvimento em paralelo: Quando desenvolvemos diferentes partes do aplicativo separadamente, podemos ter equipes especializadas em cada funcionalidade trabalhando em separado, permitindo ter um projeto finalizado em um prazo muito mais curto e o conhecimento da equipe não precisa se aprofundar em camadas que não são de sua responsabilidade.

Existem vários exemplos de aplicações onde podemos ter os designs mais comuns que são:

  • “Clean Architecture”
exemplo de projeto “Clean Architecture”
  • “DDD”
exemplo de estrutura de projeto DDD

Projeto exemplo:

Abaixo temos um exemplo de um projeto real estruturado em vários projetos separados:

Neste projeto de exemplo, foi feita uma separação em pastas numeradas para ajudar na explicação.

  1. Presentation — Nesta pasta, temos o projeto “BackendCore.Api” trata-se do projeto responsável pela implementação da API, é um projeto .NET 6 onde implementa-se uma API, nele temos referências estabelecidas para outros projetos, aos quais ele vai utilizar para realizar suas tarefas, sendo eles: “Service” e “Domain”.
  2. Service — Nesta pasta, temos o projeto “Service” onde ele é responsável pelo fornecimento das informações para o projeto da Api, sendo ele o responsável por obtenção de informações do banco de dados e tratamento de regras de negócio, o projeto “Api” somente solicita as informações ao serviço, mas o serviço transforma objetos enviados, valida antes de enviar ao banco de dados, assim como faz o caminho inverso quando os obtém de lá. É importante detalhar que a API não precisa se preocupar com as tarefas feitas aqui, somente pede e recebe. Este projeto possui referências para os projetos “Domain”, “Repository.MySql” e “Cross”.
  3. Domain — Nesta pasta, temos o projeto “Domain”, onde definimos as classes do domínio da aplicação, onde iremos além disso, estabelecer aqui os métodos de validação das mesmas, mantendo aqui de fato as informações do “Domínio da aplicação” como o próprio nome diz. Este projeto não possui referência à nenhum outro projeto, pois o domínio é o centro da aplicação.
  4. Repository — Nesta pasta, temos o projeto “Repository.MySql” onde é estabelecida a montagem do padrão Repository para acesso à dados, utilizando Entity Framework para acesso ao banco, lembrando-se que os outros projetos não precisam ter conhecimento das regras, nem também de “qual banco de dados” estamos utilizando, tanto que neste projeto há um histórico de testes realizados com o banco “SqlServer” também no início da sua criação, onde através das interfaces idênticas, pudemos simplesmente trocar o projeto de banco de dados que queremos utilizar. Neste projeto, somente há referências para o Projeto “Domain” pois ele não precisa conhecer os outros projetos.
  5. CrossCutting — Nesta pasta, temos o projeto de nome “Cross” onde ele tem o papel de encaixar funcionalidades que serão utilizadas por todas as camadas da aplicação, como Logging, Autenticação, Gestão de Exceções, Localização e Internacionalização, Gerenciamento de Configuração, Cache entra outras funcionalidades, muitas vezes também é chamado de “ToolKit” em alguns projetos que encontrei, e não possui referências para os outros projetos da aplicação.
  6. Tests — Aqui temos 2 projetos: “DomainTests” e “ServiceTests” onde criamos os testes de unidade, testes de integração para cada projeto, ficando muito mais simples e limpo deste modo devido à cada projeto se preocupar com suas exclusivas funcionalidades.

Isto não é uma regra, trata-se mais da questão de organização de cada equipe e desenvolvedor, assim como não se deve levar isto ao pé da letra para cada aplicação que desenvolvemos, pois muitas vezes temos que pesar se aquele projeto tem uma complexidade que justifique a estruturação neste formato, pois algumas coisas são tão simples que você perderá mais tempo estruturando estas camadas do que efetivamente escrevendo o código que será executado.

Outras referências

Podemos ver que alguns projetos disponíveis para o público, que muitas vezes servem como base de estudos e referências no .NET possuem estruturas de projetos muito similares e podem variar nas suas definições. Um exemplo é o projeto de referência “Equinox” do também MVP Eduardo Pires que está disponível no github, e possui uma estrutura bastante similar, sendo a principal diferença a camada “Application” onde utiliza-se para transformar os “Contratos” recebidos pela api em objetos de “Domínio” assim como o contrário, além de aplicação de regras de validação dos contratos. Além de conter outras Camadas de CrossCutting.

Existem vários projetos de referências, muitos podem ser encontrados neste repositório (link) do também Mvp Henrique Mauri.

Para quem quiser montar um projeto, tem o post do Alex que também demonstra como implementar um sistema multicamadas do zero. (link).

Quem quiser um template de um projeto Clean Architecture segue o link: ardalis/CleanArchitecture: Clean Architecture Solution Template: A starting point for Clean Architecture with ASP.NET Core (github.com).

Finalizando

Não existe “a melhor arquitetura”, tudo depende do que você está desenvolvendo, complexidade, conhecimento do time, tecnologia envolvida, dentre outras variáveis.

Portanto montar a estrutura de uma solução com múltiplos projetos vai depender da maturidade para definir o que exatamente é necessário e o que é exagero, pois pensando em software devemos levar em conta sempre a velha frase “o ótimo é inimigo do bom” e as vezes se fizermos um “over-engeneering” criando complexidade sem precisar estaremos aumentando custos e criando uma aplicação que no futuro será muito mais trabalhosa de se dar manutenção.

Gostou do artigo? clique no ícone👏e me siga para ver as próximas publicações !! Quer ver mais conteúdos, acesse minhas redes através do Linktree: https://linktree.com/nizzola

--

--

Marcio Nizzola

Microsoft MVP | Software Architect na CI&T | Prof. da Etec Itu | Membro Fundador da Comunidade Itu Developers.