.net - tortoise - visualsvn license key
¿Cómo maneja múltiples versiones del mismo software para cada cliente? (7)
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 base de datos) y un proyecto de servicio.
El proyecto de servicio utiliza el proyecto de repos y el proyecto de servicio es el proyecto con reglas de negocio.
¿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 / compilar el archivo 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.
Coloque el código específico del cliente en proyectos / conjuntos separados. Algo como el patrón de estrategia o los complementos podrían ser el camino a seguir.
La otra forma menos 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 predeterminado (por ejemplo, cuando se llama a una acción
Save
, lo primero que ocurre dentro es una llamada aOnSaveHandler
). - El controlador predeterminado no hace nada, solo devuelve "continueWithNormalExecution". Todos los controladores están en un módulo diferente al de la aplicación original (ensamblaje diferente), llamémoslo
BehaviourModule
- En las solicitudes basadas en el cliente, modificamos este
BehaviourModule
anulando el comportamiento predeterminado ''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 controladores implementando esta interfaz, por ejemplo,DoStuffInterface
, elBehaviourModule
se analiza en tiempo de carga mediante reflexión y todos los controladores que implementenDoStuffInterface
se registrarán en el sistema. Luego, en la aplicación original tendremos algo así como: SiGetDoStuffInterfaceHandler(handlerID) isnot Nothing
entoncesGetDoStuffInterfaceHandler(handlerID).DoStuff()
. Definir qué handlerId usar es configurable (podría ser a través de una tabla db, archivo xml, etc.).Terminamos teniendo múltiples controladores implementando
DoStuffInterface
con diferentes ID y llamándolos en diferentes momentos.
Con este enfoque tenemos:
- la aplicación básica con el comportamiento predeterminado
- un módulo 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 insertar ganchos allí.
Espero haber sido claro en mi descripción, si no ... deja un comentario :)
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 rama, puede fusionarse de nuevo en la línea principal para su posterior propagación a las ramas de los otros clientes. Si bien esto puede parecer el mejor enfoque, puede ser difícil de mantener y mantener un registro de qué rama 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 conjunto, uno por cliente. Esto se configura luego, tal vez mediante el uso de inyección de dependencia, 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.
Si no es gran cosa, iría con el ajuste de aplicación y el patrón de fábrica. O montajes específicos por cliente.
Pero desde las etiquetas 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.
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 inyección de dependencia son una forma.
Otra opción es usar #if ... #endif .
#if CustomerA
... do x ...
#else
... do y ...
#endif
Tendrá que ajustar sus scripts de compilación para compilar los binarios de clientes específicos. p.ej:
msbuild mysolution.sln /property:DefineConstants="CustomerA"
msbuild mysolution.sln /property:DefineConstants="CustomerB"