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

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

Обновление:

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

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

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

23
задан sashoalm 26 September 2013 в 13:25
поделиться

7 ответов

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

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

5
ответ дан 29 November 2019 в 02:52
поделиться

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

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

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

При таком подходе у нас есть:

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

Сложность такого подхода заключается в том, чтобы найти "сладкие точки" - поведение, которое клиент может захотеть настроить, и вставить туда хуки.

Надеюсь, я был понятен в своем описании, если нет... оставьте комментарий :)

5
ответ дан 29 November 2019 в 02:52
поделиться

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

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

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

#if CustomerA

    ... do x ...

#else

    ... do y ...

#endif

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

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

msbuild mysolution.sln /property:DefineConstants="CustomerB"
0
ответ дан 29 November 2019 в 02:52
поделиться

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

0
ответ дан 29 November 2019 в 02:52
поделиться

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

1
ответ дан 29 November 2019 в 02:52
поделиться

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

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

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

11
ответ дан 29 November 2019 в 02:52
поделиться

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

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

1
ответ дан 29 November 2019 в 02:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: