Pregunta sobre dependency-injection, asp.net-mvc-3, architecture, tfs, visual-studio-2010 – ¿Cuál es una buena estructura de solución para permitir una fácil personalización de un producto por cliente?

10

Estoy buscando algunos consejos sobre cómo permitir una fácil personalización y extensión de un producto central por cliente. Sé que es probablemente una pregunta demasiado grande. Sin embargo, realmente necesitamos obtener algunas ideas, ya que si la configuración de este error nos causara problemas durante años. No tengo mucha experiencia en personalizar y ampliar productos existentes.

Tenemos un producto central que generalmente diseñamos a medida por cliente. Recientemente hemos reescrito el producto en C # 4 con una interfaz MVC3. Hemos refaccionado y ahora tenemos 3 proyectos que componen la solución:

Proyecto de dominio central (espacio de nombres - nombre de proyecto .dominio. *) - que consiste en modelos de dominio (para uso de EF), interfaces de servicio de dominio, etc. (interfaces de repositorio)Proyecto de infraestructura de dominio (namespace -projectname.infrastructure. *) - que implementa el servicio de dominio-Contexto EF, implementación en Repository, implementaciones de interfaz de carga / descarga de archivos, etc.MVC3 (namespace - projectname.web. *) - proyecto que consta de controladores, modelos de vista, CSS, contenido, scripts, etc. También tiene IOC (Ninject) que maneja DI para el proyecto.

Esta solución funciona bien como un producto independiente. Nuestro problema es ampliar y personalizar el producto por cliente. Por lo general, nuestros clientes desean que se les entregue la versión principal del producto muy rápidamente (generalmente dentro de un par de días a partir de la firma de un contrato) con CSS y estilo de marca. Sin embargo, el 70% de los clientes quiere que las personalizaciones cambien la forma en que funcionan. Algunas personalizaciones son pequeñas, como propiedades adicionales en el modelo de dominio, el modelo de vista y la vista, etc. Otras son más significativas y requieren modelos de dominio y controladores completamente nuevos, etc.

Algunas personalizaciones parecen ser útiles para todos los clientes, por lo que periódicamente nos gustaría cambiarlas para que sean personalizaciones y agregarlas al núcleo.

Actualmente estamos almacenando el código fuente en TFS. Para iniciar un proyecto, normalmente copiamos manualmente la fuente en un nuevo Proyecto de equipo. Cambie el espacio de nombres para reflejar el nombre de los clientes y comenzar a personalizar las partes básicas y luego implementarlo en Azure. Obviamente, esto da como resultado una base de código completamente duplicada y estoy seguro de que no es la forma correcta de hacerlo. Creo que probablemente deberíamos tener algo que proporcione las funciones principales y se amplíe / anule cuando sea necesario. Sin embargo, realmente no estoy seguro de cómo hacerlo.

Así que estoy buscando algún consejo sobre la mejor configuración de proyecto que permita:

Rápida implementación del código: es muy fácil comenzar con un nuevo cliente para permitir cambios de marca / menoresEvitar la necesidad de copiar y pegar el código.Uso de la mayor cantidad de DI posible para mantenerlo acoplado librementePermitir que el código sea hecho a medida por clienteLa capacidad de extender el producto central en un solo lugar y hacer que todos los clientes obtengan esa funcionalidad si obtenemos la última versión del núcleo y re-implementamos

Cualquier ayuda / consejo es muy apreciado. Feliz de agregar más información que cualquiera piense que ayudará.

Después de intentar la ramificación por cliente para 5 proyectos nos dimos por vencidos. Ahora lo hemos escrito como un solo producto que utiliza MEF y DI para reunirlo en función de la configuración del cliente. También significa que podemos implementarlo en un único servidor web y se escala mejor. GraemeMiller
Mira esto en multi-tenancy que cubre bastante la situacióncodeofrob.com/entries/… GraemeMiller

Tu respuesta

2   la respuesta
8

pero aquí hay algunos consejos:

No copie su código, nunca, sea cual sea la razón.No cambie el nombre del espacio de nombres para identificar una versión de cliente dada. Utilice las ramas y la integración continua para eso.Elija un modelo de bifurcación como el siguiente: una bifurcación llamada "Principal", luego cree una bifurcación desde la versión principal de su producto, luego una bifurcación por cliente. Cuando desarrolle algo, apunte desde el principio en qué rama desarrollará según lo que esté haciendo (una característica específica del cliente irá en la rama del cliente, una versión global en la rama de la versión o la rama del cliente si desea hacer un prototipo). al principio, etc.)Haga lo mejor posible para confiar en el elemento de trabajo para realizar un seguimiento de las características que desarrolla para saber en qué sucursal se implementa para facilitar la combinación entre las sucursales.

Apuntar a la rama correcta para su desarrollo es lo más importante, no tiene que definir algunas reglas difíciles de "qué hacer en qué ocasión", sino tratar de ser coherente.

He trabajado en un gran proyecto de 10 años con más de 75 versiones y lo que solíamos hacer era:

Próxima versión importante: crea una nueva rama desde Main, dev InsidePróxima versión secundaria: dev en la rama principal actual, use Etiquetas para marcar cada versión menor dentro de su rama.Se desarrollaron algunas funciones funcionales complejas en la rama del cliente que lo solicitó, y luego se revirtió e integró en la versión de la versión cuando tuvimos éxito en "sin marca".Corrección de errores en la sucursal del cliente, luego se informa en otras sucursales cuando sea necesario. (tienes que usar el elemento de trabajo para eso o te perderás fácilmente).

Según mi opinión, otros pueden tener un punto de vista diferente, confié mucho en el elemento de trabajo para la trazabilidad del código, lo que ayudó mucho para la entrega y el informe del código.

EDITAR

Ok, agrego algunos pensamientos / comentarios sobre las ramas:

En Software Configuration Management (SCM) tiene dos funciones para ayudarlo con las versiones: sucursales y etiquetas. Cada uno no es mejor ni peor que el otro, depende de lo que necesites:

Una etiqueta se usa para marcar un punto en el tiempo, usando una etiqueta, para que luego pueda volver a ese punto si es necesario.Una rama se utiliza para "duplicar" su código para poder trabajar endos versiones al mismo tiempo.

Así que usar ramas solo depende de lo que quieras poder hacer. Si tiene que trabajar una muchas versiones diferentes (digamos una por cliente) al mismo tiempo: no hay otra manera de lidiar con eso que usar sucursales.

Para limitar el número de sucursales, debe decidir qué será una nueva sucursal o qué se marcará con una etiqueta para: Versiones específicas del cliente, Versión principal, Versión menor, Paquete de servicio, etc.

Usar ramas para las versiones de cliente parece ser un pan comido. Usar una rama para cada versión principal puede ser la elección más difícil de hacer. Si elige usar solo una rama para todas las versiones principales, entonces no tendrá la flexibilidad de trabajar en diferentes versiones principales al mismo tiempo, pero su número de sucursales será el más bajo posible.

Finalmente, Jemery Thompson tiene un buen punto cuando dice que no todo su código debe depender del cliente, hay algunas bibliotecas (normalmente las de nivel más bajo) que no deben personalizarse por cliente. Lo que normalmente hacemos es usar una rama separada (que no es por cliente) para las bibliotecas de servicios de bajo nivel, transversales y de marco. Luego haga referencia a estos proyectos en los proyectos de versión por cliente.

Mi consejo para usted es usar Nuget para estas bibliotecas y crear un paquete nuget para ellas, ya que es la mejor manera de definir las dependencias versionadas. Definir un paquete Nuget es realmente sencillo, así como configurar un servidor Nuget local.

+1 buen resumen para una buena pregunta jim tollan
Realmente aprecio la adición. Este es el enfoque que creo que vamos a tomar. También podemos considerar la modularización tanto como sea posible. Los paquetes Nuget para algunas de las funcionalidades también podrían ser un muy buen enfoque. He querido hacer eso para algunas de nuestras funciones de ayuda que se comparten entre soluciones. GraemeMiller
Las sucursales no cuestan mucho en TFS, por lo que su única preocupación es cómo administrar la cantidad de manera eficiente. Nock
Gracias por su respuesta. Puedo ver la ramificación tiene sentido. Si no cambio el espacio de nombres para identificar la versión del cliente, eso lo haría mucho más fácil. Por alguna razón, acabo de imaginar un núcleo con versiones heredadas de las versiones a medida. Bien podría ser que la simple ramificación y reintegración de las personalizaciones en el núcleo tenga más sentido. Solo me preocupaba que con 30 o 40 versiones (la mayoría de las cuales no son tan diferentes) la ramificación fuera agregando complejidad. GraemeMiller
Buena suerte para ti. Una vez que se implementa la parte de control de código fuente, intente confiar en el elemento de trabajo, ¡los enlaces escritos con versión de TFS 2010 son excelentes! Nock
4

I just worried that with 30 or 40 versions (most of which aren't that different) branching was adding complexity.

+1 Buena pregunta, es más una decisión de negocios que tendrás que tomar:

¿Quiero una base de código ordenada donde el mantenimiento sea fácil y las funciones y correcciones se implementen rápidamente para todos nuestros clientes?

¿O quiero una gran cantidad de instancias de una base de código dividida, cada una con pequeños ajustes que es difícil(EDITAR: a menos que sea un MVP de ALM que pueda "anular la marca") para fusionarse en un tronco.

Estoy de acuerdo con casi todo lo que se menciona en @Nockawa, excepto el IMHO, no sustituyo la extensión de la arquitectura de su código con sucursales.

Definitivamente use una estrategia de rama / troncal, pero como mencionó, muchas ramas hacen que sea más difícilquickly Despliegue las características del sitio y obstaculice la integración continua en todo el proyecto. Si desea evitar copiar / pegar limite el número de sucursales.

En términos de una solución de codificación, aquí está lo que creo que estás buscando:

¡Los módulos / complementos, interfaces y DI están en lo correcto!Derivación de clases personalizadas fuera de las de base (extensión del DSL por cliente, Assembly.Load ())Solución de informes personalizada (en lugar de nuevas páginas, muchas solicitudes personalizadas pueden ser informes)Páginas con hojas de cálculo (jeje, lo sé, pero curiosamente, ¡funciona!)

Grandes ejemplos del módulo / punto de complemento son los CMS, comoDotNetNuke o Kentico. Se podrían obtener otras ideas mirando la arquitectura de complementos de Facebook, los complementos para la edición de audio y video, las aplicaciones de modelado 3D (como 3DMax) y los juegos que le permiten construir sus propios niveles.

La solución ideal sería una aplicación de administración en la que pueda elegir sus módulos (DLL), adaptar el CSS (skin), crear una secuencia de comandos de dB y desplegar automáticamente la solución hasta Azure. Para lograr este objetivo, el complemento tendría mucho más sentido, el código base no se dividirá. Además, cuando se realiza una mejora en un módulo, puede extenderlo a todos sus clientes.

Fácilmente podría hacer pequeñas personalizaciones, como propiedades adicionales en el modelo de dominio, el modelo de vista y la vista, etc. con controles de usuario, clases derivadas y anulaciones de funciones.

Hágalo de forma realmente genérica, diga que un cliente dice que quiero una etiqueta que tenga la misma edad que todos en el sistema, haga una función llamadaint SumOfField(string dBFieldName, string whereClause) y luego, para ese sitio de clientes, tenga una etiqueta que se una a la función. Luego diga que otro cliente desea una función para contar la cantidad de compras de productos por cliente, puede reutilizarla: SumOfField ("product.itemCount", "CustomerID = 1").

Los cambios más significativos que requieren modelos de dominio y controladores totalmente nuevos, etc., se adaptarían a la arquitectura de los complementos. Un ejemplo podría ser que un cliente necesita un segundo campo de dirección, usted podría modificar el control de usuario de su Dirección actual para que sea un complemento de cualquier página, tendría configuraciones para saber qué tabla de dB y qué campos puede implementar su interfaz para las operaciones CRUD. .

Si la funcionalidad se personaliza por cliente en 30-40, la capacidad de mantenimiento de las sucursales será muy difícil, ya que tengo la sensación de que no podrá fusionarlas (fácilmente). Si existe la posibilidad de que esto se haga realmente grande, no desea administrar 275 sucursales. Sin embargo, si es lo que tiene que especializarse, debe bajar al nivel de control de usuario para cada cliente y "los usuarios no pueden diseñar sus propias páginas", por lo que la estrategia de ramificación de Nockawa para el front-end es perfectamente razonable.

Después de 5 clientes con el enfoque ramificado nos dimos por vencidos. Fue una pesadilla. Nuestro producto principal cambiaba constantemente y requería fusiones constantes. Estamos implementando un enfoque modular utilizando DI y también utilizando MEF. Lo estamos implementando en un solo producto y dejamos que la configuración informe qué módulos / personalizaciones se incluyen. Fue mucho más fácil. Solo requiere un poco más de reflexión en torno al diseño. GraemeMiller
Gran respuesta también. Creo que voy a tomar ambos enfoques. Estoy buscando a Orchard para inspirarme en cómo hacer una aplicación modular. Voy a escribir otra pregunta sobre eso y probablemente le daré una recompensa. Inicialmente, aunque me estoy enfocando en la bifurcación, ya que es el lugar más sencillo para comenzar, ya que solo tenemos unos pocos clientes que se mudan a la nueva base de código. Una vez que vea dónde están ocurriendo las divergencias, buscaré convertir esas partes en módulos y luego ramificarlas, etc. GraemeMiller

Preguntas relacionadas