У меня есть исходный код, который составляет 95% то же для всех клиентов. Некоторые клиенты просят что-то определенное, как бы то ни было. Как я могу управлять этим, действительно ли это возможно с VisualSVN/Subversion?
Некоторые детали о приложении, это - веб-ASP.NET MVC с NHibernate.
Приложение имеет несколько проектов: веб-часть, repo часть (где мы используем NHibernate для доступа к базе данных), и сервисный проект.
Сервисный проект использует repo проект, и сервисный проект является проектом с бизнес-правилами.
Разместите код, специфичный для клиента, в отдельных проектах/сборках. Здесь может подойти что-то вроде шаблона стратегии или плагинов.
Другим менее привлекательным способом (IMO) было бы создание отдельных ветвей для каждого клиента, но это может быстро стать трудным для поддержки.
Мы применили следующий подход:
Save
первое, что происходит внутри, это вызов OnSaveHandler
). BehaviourModule
BehaviourModule
, переопределяя стандартное поведение "ничего не делать". Код возврата этого модифицированного обработчика может быть: ContinueNormalExecution
, SkipNormalExecution
, TerminateExecution
, etc ... В других случаях мы вставляем крючки, основанные на интерфейсах. В BehaviourModule
у нас будет больше обработчиков, реализующих этот интерфейс, например, DoStuffInterface
, BehaviourModule
разбирается во время загрузки с помощью рефлексии и все обработчики, реализующие DoStuffInterface
, регистрируются в системе.
Тогда в исходном приложении мы будем иметь что-то вроде: If GetDoStuffInterfaceHandler(handlerID) isnot Nothing
then GetDoStuffInterfaceHandler(handlerID).DoStuff()
. Определение того, какой handlerId использовать, является конфигурируемым (может быть через таблицу db, xml файл, и т.д.).
В итоге мы имеем несколько обработчиков, реализующих DoStuffInterface
с разными ID и вызывающих их в разное время.
При таком подходе у нас есть:
Сложность такого подхода заключается в том, чтобы найти "сладкие точки" - поведение, которое клиент может захотеть настроить, и вставить туда хуки.
Надеюсь, я был понятен в своем описании, если нет... оставьте комментарий :)
Использование системы управления версиями для решения этой проблемы, вероятно, вызовет больше проблем, чем решит.
Предложения других пользователей разделить клиентский код на отдельные сборки и/или использовать внедрение зависимостей — это один из способов.
Другой вариант - использовать #if ...#endif.
#if CustomerA
... do x ...
#else
... do y ...
#endif
Вам нужно будет настроить сценарии сборки для сборки конкретных двоичных файлов клиента. eg:
msbuild mysolution.sln /property:DefineConstants="CustomerA"
msbuild mysolution.sln /property:DefineConstants="CustomerB"
Разница в 5% заключается только в том, что только на основе пользовательского интерфейса или также бизнес-логики? Если пользовательский интерфейс основан на пользовательском интерфейсе, то следует сменить слой пользовательского интерфейса и отправить/скомпилировать файл пользовательского интерфейса с приложением. Если бизнес-логика, то это сложнее. Возможно, ветвление (через SVN) может помочь. Но все же хлопоты с текущей разработкой приложения, поэтому не советуют.
Полезным дополнением к #ifdef ACME / # endif и т. Д. Является определение макросов для макросов ACME_ONLY (), NON_ACME (), FROBOZCO_ONLY (), NON_FROBOZCO () и т. Д. Материал все равно может стать беспорядочным, если в игру вступят новые версии (в каких случаях новая версия должна вести себя как Acme, FrobozCo и т. Д.), Но если есть только одна разница между версией Acme и версией, отличной от Acme, этот подход позволяет избежать окружения этого на две строки #directives.
Я могу предложить два подхода, которые могут сработать.
Первый предполагает разветвление кода для каждого клиента. Любая правка, которую вы делаете в основной линии, может быть интегрирована в ветку конкретного клиента, когда это необходимо. Аналогично, если что-то в основном продукте исправлено в ветке, это может быть объединено обратно в основную линию для последующего распространения в ветки других клиентов. Хотя такой подход может показаться наилучшим, его может быть трудно поддерживать, а отслеживание того, в какой ветке какие правки были внесены, может стать проблематичным.
Второй, и, возможно, лучший подход, включает в себя рефакторинг кода таким образом, чтобы код для конкретного клиента находился в одной сборке - по одной для каждого клиента. Затем она конфигурируется, возможно, с помощью инъекции зависимостей, при установке продукта. Таким образом, у вас будет только одна строка кода и никакого слияния между ветвями. Хотя это зависит от того, что код для конкретного клиента можно легко отделить.
Если это не проблема, я бы выбрал настройки приложения и заводские шаблоны. Или конкретные сборки для каждого клиента.
Но, судя по тегам, вы хотите решить эту проблему с помощью контроля версий. Но это сильно ударит по слиянию и т. Д. Вам нужно будет создать ветку для каждого клиента и слить с ними изменения из транка.