Подтвердить что ты не робот

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

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

Обновление:

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

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

Проект службы использует проект репо, а проект службы - это проект с бизнес-правилами.

4b9b3361

Ответ 1

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

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

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

Ответ 2

Поместите конкретный код клиента в отдельные проекты/сборки. Возможно, что-то вроде шаблона стратегии или плагинов.

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

Ответ 3

Подход, который мы сделали, заключается в следующем:

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

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

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

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

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

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

Ответ 4

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

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

Ответ 5

Полезным дополнением к #ifdef ACME/# endif и т.д. является определение макросов для макросов ACME_ONLY(), NON_ACME(), FROBOZCO_ONLY(), NON_FROBOZCO() и т.д. Материал может по-прежнему запутываться, если в игру вступают новые версии (в каких случаях должна вестись новая версия, например, Acme, FrobozCo и т.д.), Но если есть только одна разница между версией Acme и non-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"