Как вы управляете несколькими версиями одного и того же программного обеспечения для каждого клиента?

#.net #version-control #visualsvn #svn #visualsvn-server

#.net #управление версиями #visualsvn #svn #visualsvn-сервер

Вопрос:

У меня есть исходный код, который на 95% одинаков для всех клиентов. Однако некоторые клиенты запрашивают что-то конкретное. Как я могу управлять этим, возможно ли это с помощью VisualSVN / Subversion?

Обновить:

Некоторые подробности о приложении, это веб ASP.NET MVC с NHibernate.

Приложение имеет несколько проектов: веб-часть, часть репозитория (где мы используем NHibernate для доступа к базе данных) и сервисный проект.

Сервисный проект использует проект repo, а сервисный проект — это проект с бизнес-правилами.

Комментарии:

1. хороший вопрос. конечно, с нетерпением ждем отзывов от всех.

Ответ №1:

Я могу придумать два подхода, которые могли бы сработать.

Первый включает в себя разветвление кода для каждого клиента. Любое изменение, которое вы вносите в основную строку, затем может быть интегрировано в ветку конкретного клиента, когда это необходимо. Аналогично, если что-то в основном продукте исправлено в ветке, это может быть объединено обратно в основную линию для последующего распространения в ветках других клиентов. Хотя это может показаться наилучшим подходом, его может быть сложно поддерживать, и отслеживание того, в какой ветке есть какие изменения, будет сопряжено с трудностями.

Второй и, возможно, лучший подход предполагает рефакторинг вашего кода таким образом, чтобы код, специфичный для клиента, находился в одной сборке — по одной на каждого клиента. Затем это настраивается, возможно, с помощью внедрения зависимостей, при установке продукта. Таким образом, у вас есть только одна строка кода и нет слияния между ветвями. Хотя это зависит от того, что код, специфичный для клиента, легко отделяется.

Ответ №2:

Размещайте специфичный для клиента код в отдельных проектах / сборках. Возможно, подойдет что-то вроде шаблона стратегии или подключаемых модулей.

Другим, менее привлекательным способом (IMO) было бы создание отдельных ветвей для каждого клиента, но это может быстро стать сложным в обслуживании.

Комментарии:

1. Первое предложение, да, но вместо плагинов я предпочитаю помещать общий код в библиотечный проект. Конечно, это полностью зависит от конкретного проекта.

2. Я хотел бы предложить, чтобы, если вы переключитесь с subversion, проблема ветвления в основном исчезнет. Тем не менее, я бы все равно попытался разделить части приложения, которые клиенты в основном хотят настроить. Однако я бы рекомендовал несколько филиалов.

Ответ №3:

Мы применили следующий подход:

  • Вставьте перехваты внутри приложения, позволяющие настраивать поведение по умолчанию (например, при вызове Save действия первое, что происходит внутри, — это вызов OnSaveHandler ).
  • Обработчик по умолчанию ничего не делает, он просто возвращает «continueWithNormalExecution». Все обработчики находятся в другом модуле, отличном от исходного приложения (другой сборки), давайте назовем это BehaviourModule
  • При клиентских запросах мы изменяем это BehaviourModule , переопределяя поведение по умолчанию «ничего не делать». Код возврата этого измененного обработчика может быть следующим: ContinueNormalExecution , SkipNormalExecution , TerminateExecution и т.д…
  • В других случаях мы вставляем перехваты на основе интерфейсов. В BehaviourModule у нас будет больше обработчиков, реализующих этот интерфейс, например, DoStuffInterface , BehaviourModule анализируется во время загрузки с использованием отражения, и все реализующие обработчики DoStuffInterface будут зарегистрированы в системе. Тогда в исходном приложении у нас будет что-то вроде: Если GetDoStuffInterfaceHandler(handlerID) isnot Nothing то GetDoStuffInterfaceHandler(handlerID).DoStuff() . Определение того, какой идентификатор handlerId использовать, настраивается (может быть с помощью таблицы db, xml-файла и т.д.).

    В итоге мы получаем несколько обработчиков, реализующих DoStuffInterface разные идентификаторы и вызывающих их в разное время.

При таком подходе мы имеем:

  • базовое приложение с поведением по умолчанию
  • настраиваемый модуль (сборка) настраивает способ работы приложения

Проблема при таком подходе заключается в поиске «приятных моментов» — моделей поведения, которые клиент, возможно, захочет настроить, и вставке в них перехватов.

Надеюсь, я ясно изложил свое описание, если нет … оставьте комментарий 🙂

Ответ №4:

Если это не имеет большого значения, я бы выбрал настройку appp и заводской шаблон. Или конкретными сборками для каждого клиента.

Но, судя по тегам, вы хотите решить проблему с помощью контроля версий. Но это сильно ударит по слиянию и т.д. Вам нужно будет создать ветку для каждого клиента и объединить изменения из trunk в них.

Ответ №5:

Полезным дополнением к #ifdef ACME / #endif и т.д. Является определение макросов для ACME_ONLY(), NON_ACME(), FROBOZCO_ONLY(), NON_FROBOZCO() и т.д. макросов. При появлении новых версий все еще может запутаться (в каких случаях новая версия должна вести себя как Acme, FrobozCo и т.д.), Но если разница между версией Acme и версией, отличной от Acme, составляет всего одну строку, этот подход позволяет избежать окружения этой строки двумя строками директив #.

Ответ №6:

Разница в 5% заключается только в пользовательском интерфейсе или также в бизнес-логике? Если основан на пользовательском интерфейсе, вам следует ускорить уровень пользовательского интерфейса и отправить / скомпилировать соответствующий файл пользовательского интерфейса вместе с приложением. Если использовать бизнес-логику, то это сложнее. Возможно, ветвление (через SVN) могло бы помочь. Но все еще возникают проблемы с текущей разработкой приложения, поэтому не рекомендуется.

Ответ №7:

Использование контроля версий для решения этой проблемы, вероятно, вызовет больше проблем, чем решит.

Предложения других разделить специфичный для клиента код на отдельные сборки и / или использовать внедрение зависимостей — это один из способов.

Другой вариант — использовать #if … #endif.

 #if CustomerA

    ... do x ...

#else

    ... do y ...

#endif
  

Вам нужно будет настроить свои сценарии сборки для сборки конкретных двоичных файлов клиента. например:

 msbuild mysolution.sln /property:DefineConstants="CustomerA"

msbuild mysolution.sln /property:DefineConstants="CustomerB"
  

Комментарии:

1. Это решение не будет хорошо масштабироваться, что делать, если у вас 5 или 10 клиентов. Цель кода была бы потеряна во всех #ifs

2. Не зная, что такое 5%, невозможно сказать, какой метод правильный. Просто подбрасываю другой вариант.