¿Cómo maneja múltiples versiones del mismo software para cada cliente?

Tengo un código fuente que es 95% el mismo para todos los clientes. Sin embargo, algunos clientes piden algo específico. ¿Cómo puedo gestionar esto? ¿Es posible con VisualSVN / Subversion?

Actualizar:

Algunos detalles sobre la aplicación, es un ASP.NET MVC web con NHibernate.

La aplicación tiene varios proyectos: la parte web, la parte repo (donde usamos NHibernate para acceder a la database) y un proyecto de service.

El proyecto de service utiliza el proyecto de repos y el proyecto de service es el proyecto con reglas de negocio.

Puedo pensar en dos enfoques que podrían funcionar.

El primero implica ramificar el código para cada cliente. Cualquier edición que realice en la línea principal se puede integrar en la sucursal del cliente específico cuando sea necesario. De forma similar, si algo en el producto central se fija en una twig, puede fusionarse de nuevo en la línea principal para su posterior propagación a las twigs de los otros clientes. Si bien esto puede parecer el mejor enfoque, puede ser difícil de mantener y mantener un logging de qué twig tiene qué ediciones se llenan de tensión.

El segundo enfoque, y tal vez el mejor, implica la refacturación de su código para que el código específico del cliente se encuentre en un único set, uno por cliente. Esto se configura luego, tal vez mediante el uso de dependency injection, cuando el producto está instalado. De esta forma, solo tienes una línea de código y no se fusiona entre sucursales. Aunque depende de que el código específico del cliente se pueda separar fácilmente.

Coloque el código específico del cliente en proyectos / sets separados. Algo como el patrón de estrategia o los complementos podrían ser el path a seguir.

La otra forma less atractiva (IMO) sería crear sucursales separadas para cada cliente, pero esto puede volverse difícil de mantener rápidamente.

El enfoque que hemos tomado es:

  • Inserte ganchos dentro de la aplicación que permite personalizar el comportamiento pnetworkingeterminado (por ejemplo, cuando se llama a una acción Save , lo primero que ocurre dentro es una llamada a OnSaveHandler ).
  • El controller pnetworkingeterminado no hace nada, solo devuelve "continueWithNormalExecution". Todos los controlleres están en un module diferente al de la aplicación original (ensamblaje diferente), llamémoslo BehaviourModule
  • En las requestes basadas en el cliente, modificamos este BehaviourModule anulando el comportamiento pnetworkingeterminado 'no hacer nada'. El código de retorno de este manejador modificado puede ser: ContinueNormalExecution , SkipNormalExecution , TerminateExecution , etc …
  • En otros casos, insertamos ganchos basados ​​en interfaces. En el BehaviourModule tendremos más controlleres implementando esta interfaz, por ejemplo, DoStuffInterface , el BehaviourModule se analiza en time de carga mediante reflexión y todos los controlleres que implementen DoStuffInterface se registrarán en el sistema. Luego, en la aplicación original tendremos algo así como: Si GetDoStuffInterfaceHandler(handlerID) isnot Nothing entonces GetDoStuffInterfaceHandler(handlerID).DoStuff() . Definir qué handlerId usar es configurable (podría ser a través de una tabla db, file xml, etc.).

    Terminamos teniendo múltiples controlleres implementando DoStuffInterface con diferentes ID y llamándolos en diferentes momentos.

Con este enfoque tenemos:

  • la aplicación básica con el comportamiento pnetworkingeterminado
  • un module configurable (ensamblaje) que personaliza la forma en que funciona la aplicación

El desafío con este enfoque es encontrar los "puntos dulces" : comportamientos que el cliente podría querer personalizar e insert ganchos allí.

Espero haber sido claro en mi descripción, si no … deja un comentario 🙂

Si no es gran cosa, iría con el ajuste de aplicación y el patrón de fábrica. O assemblys específicos por cliente.

Pero desde las tags parece que quieres resolverlo a través del control de versiones. Pero esto supondrá un gran éxito en la fusión, etc. Deberá crear una sucursal para cada cliente y fusionar los cambios del enlace troncal con ellos.

Un adjunto útil para #ifdef ACME / # endif etc. es definir macros para las macros ACME_ONLY (), NON_ACME (), FROBOZCO_ONLY (), NON_FROBOZCO (), etc. Las cosas todavía pueden complicarse si entran en juego nuevas versiones (en cuyo caso la nueva versión se comportará como Acme, FrobozCo, etc.) pero si hay una sola línea de diferencia entre la versión Acme y la no Acme, este enfoque evita rodear ese línea por dos líneas de #directivas.

¿La diferencia del 5% es que solo se basa en la interfaz de usuario o también en la lógica comercial? Si tiene una interfaz de usuario basada, debe especificar la capa de interfaz de usuario y enviar / comstackr el file de interfaz de usuario apropiado con la aplicación. Si la lógica de negocios, esto es más complicado. Tal vez la ramificación (a través de SVN) podría ayudar. Pero sigue siendo una molestia con el desarrollo actual de la aplicación, por lo tanto, no se aconseja.

Usar el control de versiones para resolver este problema probablemente cause más problemas de los que resuelve.

Las sugerencias de otros para separar el código específico del cliente en ensamblajes separados y / o usar la dependency injection son una forma.

Otra opción es usar #if … #endif .

 #if CustomerA ... do x ... #else ... do y ... #endif 

Tendrá que ajustar sus scripts de compilation para comstackr los binarys de clientes específicos. p.ej:

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