Qual é a maneira mais eficaz de compartilhar código entre aplicativos .NET?

14

Em nosso trabalho, temos vários aplicativos .net diferentes que compartilham muita funcionalidade básica. Criamos esses aplicativos usando uma arquitetura limpa de n camadas, mas chegamos ao momento em que percebemos que reimplementamos as mesmas funções várias vezes. Obviamente, isso viola a DRY, e gostaríamos de corrigir isso. Já estamos usando o Nuget com sucesso para obter códigos comuns de cola (conectando IoC, registro, configurações), mas também gostaríamos de compartilhar nossas camadas de dados e negócios entre todos os nossos aplicativos. A ideia é que a interface do usuário lide apenas com as partes da camada de negócios que realmente precisa.

Isso parece um problema direto no começo, mas o desenvolvimento contínuo pode fornecer algumas armadilhas e não temos certeza de como proceder. Digamos que criamos nossa camada de negócios única para governá-los todos. Por uma questão de brevidade, chamarei de "Fundação". Portamos nossos aplicativos para usar a Foundation, e tudo está indo muito bem. A Fundação é distribuída para iluminar as camadas da interface do usuário via nuget, e estamos com uma boa aparência. Mas então começamos a adicionar recursos aos nossos aplicativos e encontramos problemas.

Digamos que estamos trabalhando no Projeto A e adicionamos um novo recurso que requer alterações no Foundation. Fazemos as alterações na fundação (Foundation-A) e as empurramos para o feed de pepitas como um pacote instável. O Projeto A recebe o pacote mais recente de pepitas e tudo está bem. Enquanto isso, outro desenvolvedor está trabalhando no Projeto B. Ele obtém o Foundation mais recente do controle de origem, mas o retira de uma ramificação estável, para que não tenha alterações no Projeto A. Ele faz alterações e criou a Foundation-B. E tudo está bem. Porém, descobrimos que as funcionalidades de implementação Foundation-A e Foundation-B que realmente poderiam compartilhar código, então as combinamos. Enquanto isso, a Foundation-C está flutuando com suas próprias mudanças. Eventualmente, o Foundation-B está pronto para a produção, então nós o expomos. Mas precisamos atualizar a produção A, B,

Parece que poderia funcionar, mas estamos preocupados em trabalhar com diferentes esquemas de banco de dados e manter tudo sincronizado entre as várias ramificações do repositório Foundation, bem como os repositórios do Projeto A, B e C. Parece que provavelmente será necessário muito trabalho manual, o que abre a possibilidade de erros. Eu gostaria disso o mais automatizado possível.

Aqui está a pilha que estamos usando: C #, TFS com integração contínua, Nuget. Nossos aplicativos são todos os tipos de aplicativos ASP.NET. Estamos dispostos a olhar para diferentes SCMs, se isso facilitar as coisas.

Estou procurando maneiras de manter o Nuget com os nossos diferentes ramos de código-fonte. Não queremos inserir código de desenvolvimento acidentalmente em produção porque fazemos referência ao Pacote Nuget errado.

Josh
fonte
Além disso, se este não for um bom fórum para uma discussão como essa, alguém pode sugerir um melhor?
21713 Josh

Respostas:

9

Fazemos as alterações na fundação (Foundation-A) e as empurramos para o feed de pepitas como um pacote instável.

Aqui é onde seu problema começa ... Não faça isso.

Quaisquer alterações no Foundation v1.0 devem ser inerentemente valiosas para todos os consumidores do Foundation, caso contrário, ele não pertence ao Foundation. Portanto, ao criar o pacote nuget, faça-o como uma versão oficial e estável do Foundation (por exemplo, v1.1), ou não faça nada.

O Projeto B deve criar os aprimoramentos do Foundation como faria normalmente, mas (no bom modo de gerenciamento de origem) deve mesclar-se às alterações de tronco (v1.1) antes de enviar um Foundation (v1.2) estável para o nuget.

Outros projetos que podem usar os aprimoramentos do Foundation podem atualizar suas referências de nuget, quando apropriado, ou seguir as versões mais antigas, se necessário.

Eu concordo com @Giedrius ; isso me parece ser mais um problema de controle / ramificação de origem, no sentido de que, se a ramificação / fusão do Foundation for tratada adequadamente, os problemas de gerenciamento de pacotes se tornarão discutíveis.

Eric King
fonte
Sim, isso faz sentido. O problema está na aplicação prática. Conforme você atualiza o Foundation for v1.1, você deseja usar essas alterações no Projeto B à medida que desenvolve. Se a maneira como você compartilha esse código é através do nuget, suas opções são: 1) Publique o novo pacote de nuget ou 2) Copie manualmente as DLLs. Nenhuma dessas parece ser uma boa escolha.
21713 Josh
Parte disso pode ser atenuada com uma suíte de testes eficaz, mas ainda haverá a alternância entre os recursos.
21413 Josh
@ Josh Sim, as atualizações do Foundation devem ser completas e testáveis, independentemente de como o Projeto B as usará (já que elas estão entrando em uma biblioteca compartilhada); portanto, 'publicar novo pacote de nuget' é o caminho natural a seguir. Não importe e use o Foundation v.Next no Projeto B até que seja um pacote estável no nuget. É preciso um pouco de disciplina, mas é muito melhor do que a bagunça de fazer o contrário.
Eric Rei
1
Entendo perfeitamente o que você está dizendo, mas não acho muito realista. À medida que você desenvolve um recurso, mesmo que você tenha toda a lógica de negócios e a camada de acesso a dados totalmente testada, haverá modificações antes que ele seja enviado à produção. Talvez você tenha percebido que precisa adicionar outra propriedade à lógica de negócios ou o proprietário do produto volte com alterações ou esclarecimentos. Talvez algumas de suas suposições simplificadoras estivessem erradas. São coisas que acontecem o tempo todo. Se você está limitado a travar a liberação de sua lógica base, parece muito limitado.
21413 Josh
2
Como fazemos isso há alguns meses, é basicamente nisso que decidimos. Eu acho que estava muito preocupado em ter um processo formal. Quando começamos a trabalhar nisso, as coisas acabaram indo muito bem. Obrigado pela sua contribuição.
Josh
4

Refatore seu código duplicado em funções aplicáveis ​​de uma maneira mais abstrata e coloque-as em suas próprias bibliotecas ou estruturas. Torne-os fracamente acoplados e independentes de arquitetura, e você nunca deve ter nenhum problema. Se você precisar de inspiração, estude como a estrutura .NET abstrai conceitos usando coisas como interfaces, genéricos e padrões de software como Dispose().

Além disso, lembre-se de que nem todo código duplicado é realmente duplicado; algumas são códigos de cola ou códigos necessários para sustentar a arquitetura; portanto, não fique obcecado por ficar muito seco.

Robert Harvey
fonte
Talvez eu não estivesse tão claro quanto poderia estar na minha pergunta. É sobre o que vem a seguir. Você já fez tudo isso e agora precisa compartilhar seu código efetivamente entre seus diferentes projetos. Alguém diz "basta usar o nuget" e isso soa ótimo até você entrar em detalhes e encontrar problemas - como sincronizar diferentes alterações de diferentes ramos e aplicativos.
21713 Josh
Bem, eu concordo com Eric King. O código da fundação deve ser enviado como uma versão estável, não instável. A chave é manter uma API estável: se você souber que as assinaturas dos métodos de fundação não serão alteradas, poderá enviar com segurança os métodos para o Foundation e refatorá-los mais tarde, sem quebrar nada.
Robert Harvey
4

Reutilização de código

Houve várias abordagens para a reutilização de código que foram favorecidas ao longo dos anos. Cada abordagem tem seu lugar e, mais importante, seus problemas , mas as maneiras viáveis ​​de reutilizar o código no .NET são:

  1. Biblioteca comum. O código necessário em mais de um local é colocado em uma biblioteca comum e todas as outras partes da base de código têm uma única referência a esse código. A principal desvantagem é que você acaba com a maior parte do seu projeto, dependendo dessa biblioteca, que contém muitas funções não relacionadas. Esta é uma péssima ideia do ponto de vista da garantia de qualidade.

  2. Código-fonte comum. O código necessário em mais de um local é gravado uma vez e colocado em um arquivo de origem em uma estrutura de diretório comum. Todos os projetos que precisam desse código incluem esse arquivo como um de seus arquivos de origem. Isso fornece a reutilização de código, e as vantagens de escrever uma vez usam muitos. Contudo. Sua desvantagem é que é possível ter diferentes partes do projeto compiladas com diferentes versões deste código - o que pode introduzir alguns defeitos sutis que podem ser muito difíceis de detectar e identificar pela garantia da qualidade.

  3. Serviço. O código comum é implementado como um serviço, que outros aspectos podem acessar. Isso tem a vantagem de haver um único serviço em uma implantação e evita dependências. No entanto, introduzirá latência e falhas. Esse tipo de abordagem funciona bem em grandes produtos distribuídos, onde alta disponibilidade e tolerância a falhas já são entendidas e gerenciadas.

Gerenciando o NuGET

Aqui você tem um problema muito mais interessante. Gerenciando várias configurações. Meu conselho aqui é não gerenciar uma base de clientes diversificada com diferentes versões de código, mas com arquivos de configuração. Existem pelo menos três camadas de dados de configuração para gerenciar. Configuração básica (interna) do produto que o cliente nunca vê. As opções de configuração padrão que seu cliente pode alterar e a camada final de opções de configuração que seu cliente alterou. Ao separar essas diferentes camadas, você poderá implementar atualizações sem destruir as configurações de seus clientes.

Michael Shaw
fonte
1

Acho que o problema não está no nuget / controle de origem / ramificação, mas no que entra no código de colagem.

Robert tem uma boa resposta, só para ver uma imagem completa, recomendo pensar em quais dependências esses utilitários comuns trarão em cada projeto:

http://ayende.com/blog/3986/let-us-burn-all-those-pesky-util-common-libraries http://blog.jordanterrell.com/post/CommonUtility-Libraries-Dead.aspx

A melhor maneira de evitar o inferno que você está tendo é abrir seu código de cola. Dessa forma, você começará a se importar, que nenhuma lógica de negócios seja divulgada, de modo que nenhuma dependência concreta do projeto caia no código da cola, que seria abstrato o suficiente para ser reutilizado por qualquer pessoa, mesmo fora da sua empresa - e se essa cola código será bom o suficiente - você também receberá informações da comunidade.

Giedrius
fonte
Não estou falando sobre o gerenciamento de código cola. Já temos uma solução para isso e funciona bem. Estou falando sobre o compartilhamento da lógica de negócios. Por exemplo, nossos aplicativos lidam com o gerenciamento de organizações e pessoas. Portanto, precisamos crie uma pessoa e há várias regras de negócios associadas à forma como uma pessoa deve ser criada.Em vez de criá-la várias vezes, queremos ter um pacote que lide com tudo, desde a construção do objeto até a persistência que compartilhamos entre os diferentes projetos que precisam criar pessoas
Josh
0

A solução é simples: crie um projeto separado para ele e gerencie-o como algo separado: são requisitos próprios, testes, convenções, documentação, planejamento, pessoas etc. avaliados primeiro antes de criarem um problema.

Ou melhor ainda: torne-o "open-source" dentro da sua organização. Portanto, qualquer pessoa pode alterar o código, mas apenas poucas pessoas selecionadas terão direitos completos de confirmação. E essas pessoas serão responsáveis ​​por garantir a qualidade e os recursos corretos.

Eufórico
fonte