10

Sto cercando alcuni consigli su come consentire una facile personalizzazione e l'estensione di un prodotto principale per cliente. So che probabilmente è una domanda troppo grande. Tuttavia, abbiamo davvero bisogno di avere qualche idea, come se avessimo l'impostazione sbagliata che potrebbe causarci problemi per anni. Non ho molta esperienza nella personalizzazione e nell'estensione dei prodotti esistenti.Qual è una buona soluzione per consentire una facile personalizzazione di un prodotto per cliente?

Abbiamo un prodotto principale che di solito pubblichiamo su base cliente. Abbiamo recentemente riscritto il prodotto in C# 4 con un frontend MVC3. Noi abbiamo riscritta e ora hanno 3 progetti che compongono la soluzione: (. Namespace - projectname.domain *) -

  • progetto dominio anime che consistono in modelli di dominio (per l'uso da EF), interfacce di servizio del dominio, ecc (interfacce repository)
  • progetto di infrastruttura
  • Domain (-projectname.infrastructure namespace *) -. che implementa il contesto dominio del servizio-EF, implementazione Repository, caricamento di file/interfaccia per il download implementazioni ecc
  • MVC3 (namespace -. projectname.web *) -project che consiste in controller, viewmodels, CSS, contenuto, script ecc. Ha anche IOC (Ninject) che gestisce la DI per il progetto.

Questa soluzione funziona bene come prodotto standalone. Il nostro problema è l'estensione e la personalizzazione del prodotto per cliente. I nostri clienti di solito vogliono che la versione del prodotto principale sia data loro molto rapidamente (di solito entro un paio di giorni dalla firma di un contratto) con CSS e styling personalizzati. Tuttavia, il 70% dei client desidera che le personalizzazioni cambino il modo in cui funziona. Alcune personalizzazioni sono piccole come proprietà aggiuntive su modello di dominio, viewmodel e vista ecc. Altre sono più significative e richiedono modelli di dominio e controller completamente nuovi ecc.

Alcune personalizzazioni sembrano essere utili a tutti i client, quindi periodicamente vorremmo per cambiarle da personalizzazioni e aggiungerle al nucleo.

Attualmente stiamo memorizzando il codice sorgente in TFS. Per avviare un progetto di solito copiamo manualmente la fonte in un nuovo progetto di squadra. Cambiare lo spazio dei nomi per riflettere il nome del client e iniziare a personalizzare le parti di base e quindi distribuirlo ad Azure. Questo ovviamente si traduce in una base di codice completamente duplicata e sono sicuro che non è il modo giusto per farlo. Penso che probabilmente dovremmo avere qualcosa che fornisce le funzionalità principali e che estende/sovrascrive dove richiesto. Tuttavia non sono davvero sicuro su come procedere.

quindi non vedo per qualsiasi consiglio sulla migliore configurazione di progetto che consentirebbe:

  • Implementazione rapida del codice - così facile da iniziare un nuovo client per consentire di branding/piccole modifiche
  • prevenire la necessità di copiare e incollare del codice
  • l'utilizzo di ben dI possibile per mantenerlo debolmente accoppiati
  • Consenti per bespoking del codice su un per la base di cliente
  • La possibilità di estendere il prodotto di punta in un unico luogo e hanno tutti i clienti a ottenere che la funzionalità se otteniamo la ultima versione del core e ri-distribuire

Qualsiasi aiuto/consiglio è molto apprezzato. Felice di aggiungere ulteriori informazioni che chiunque pensi possa aiutare.

+0

Dopo aver provato la ramificazione per client per 5 progetti, abbiamo rinunciato. Ora lo abbiamo scritto come un singolo prodotto che utilizza MEF e DI per combinarlo in base alla configurazione del client. Significa anche che possiamo implementarlo su un singolo host web e che si adatta meglio. – GraemeMiller

+0

Vedi questo su multi-tenancy che copre praticamente la situazione http://codeofrob.com/entries/multi-tenancy-in-asp.net-mvc---ddd8-video.html – GraemeMiller

risposta

4

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


+1 Grande questione, la sua più di una decisione di business che dovrete fare:

voglio un codice-base ordinata dove la manutenzione è facile e funzionalità e correzioni get implementato rapidamente a tutti i nostri clienti

oppure voglio una pletora di istanze di una base di codice contempla, ognuno con piccoli ritocchi che è difficile (EDIT: a meno che il vostro un ALM MVP che possono cose "unbrand") a fuso in un tr Zio.


Sono d'accordo con quasi tutto @Nockawa menzionato tranne IMHO non sostituire l'estensione dell'architettura del codice con le filiali.

Utilizzare decisamente una strategia branch/trunk ma, come hai menzionato troppi rami, rende più difficile l'implementazione delle funzionalità del sito di quickly e l'integrazione continua a livello di progetto. Se si desidera impedire la copia/incolla limitare il numero di rami.

In termini di una soluzione di codifica qui è quello che io credo che stai cercando:

  • moduli/Plug-in, interfacce e di è proprio sul bersaglio!
  • Derivazione classi personalizzate fuori quelli di base (estensione del DSL per cliente, Assembly.Load())
  • personalizzato soluzione di reporting (al posto di nuove pagine un sacco di richieste personalizzate potrebbe essere rapporti)
  • Pagine con fogli di calcolo (hehe lo so - ma stranamente funziona)

Grandi esempi di punto del modulo/plugin sono di CMS come DotNetNuke o Kentico!. Altre idee possono essere acquisite guardando l'architettura di componenti aggiuntivi di Facebook, i plug-in per l'editing audio e video, le app di modellazione 3D (come 3DMax) e i giochi che consentono di creare i propri livelli.

La soluzione ideale sarebbe un'applicazione di amministrazione che è possibile scegliere il tuo moduli (DLL), misura il CSS (pelle), script dB, e auto-distribuire la soluzione fino a Azure. Per conseguire questo obiettivo, i plug-in farebbero sì che abbia molto più senso, il codebase non sarà diviso. Inoltre, quando un miglioramento di viene eseguito su un modulo, è possibile eseguirlo su tutti i client .

È possibile eseguire facilmente personalizzazioni di piccole dimensioni come proprietà aggiuntive su modello di dominio, viewmodel e visualizzazione ecc con controlli utente, classi derivate e sostituzioni di funzioni.

Farlo in modo generico, dire che un cliente dice che voglio un'etichetta che identifica l'età di tutti nel sistema, creare una funzione chiamata int SumOfField(string dBFieldName, string whereClause) e quindi per quel sito clienti avere un'etichetta che si lega alla funzione. Quindi, se un altro cliente desidera una funzione per contare il numero di acquisti di prodotti da parte del cliente, è possibile riutilizzarlo: SumOfField ("product.itemCount", "CustomerID = 1").

Ulteriori modifiche significative che richiedono nuovi modelli di dominio e controller ecc. Si adatterebbero all'architettura plug-in. Un esempio potrebbe essere che un cliente ha bisogno di un secondo campo di indirizzo, si dovrebbe ritoccare il proprio controllo utente Address corrente come plug-in in qualsiasi pagina, avrebbe le impostazioni per sapere quale tabella dB e quali campi può implementare la sua interfaccia alle operazioni CRUD .

Se la funzionalità è personalizzato per ogni cliente in 30-40 rami manutenibilità diventerà così difficile come ho la sensazione che non sarà in grado di unire insieme (facilmente). Se c'è la possibilità che questo sia diventato veramente grande, non vuoi gestire 275 rami. Tuttavia, se è il proprio specializzato a scendere al livello di controllo utente per ogni cliente e "gli utenti non possono progettare le proprie pagine", avendo la strategia di diramazione di Nockawa per il front-end è perfettamente ragionevole .

+1

Ottima risposta pure. Prenderò entrambi gli approcci credo. Sto guardando Orchard per l'ispirazione su come rendere modulare l'applicazione. Ho intenzione di scrivere un'altra domanda al riguardo e probabilmente mettere una taglia su di essa.Inizialmente, però, mi sto concentrando sulla ramificazione, in quanto è il punto di partenza più semplice in quanto ci sono solo pochi clienti che si spostano verso la nuova base di codice. Una volta che vedo dove stanno accadendo le divergenze, cercherò di convertire quelle parti in moduli e poi dirigerle ecc. – GraemeMiller

+0

Dopo 5 clienti con l'approccio ramificato abbiamo rinunciato. È stato un incubo Il nostro prodotto principale stava cambiando costantemente e richiedeva una fusione costante. Stiamo implementando un approccio modulare usando DI e anche usando MEF. Stiamo implementando un singolo prodotto e lasciando che la configurazione informi quali moduli/personalizzazioni sono inclusi. E 'stato molto più facile. Richiede solo un po 'più di riflessione sul design. – GraemeMiller

8

non posso rispondere a questa completamente, ma ecco alcuni consigli:

  1. Non copiare il codice, sempre, qualunque sia la ragione è.
  2. Non rinominare lo spazio dei nomi per identificare una determinata versione client. Usa i rami e l'integrazione continua per quello.
  3. Scegliere un modello di diramazione come il seguente: un ramo radice chiamato "Principale", quindi creare un ramo da Principale per versione principale del prodotto, quindi un ramo per client. Quando sviluppi qualcosa, scegli fin dall'inizio in quale ramo svilupperai a seconda di ciò che stai facendo (una funzione specifica del client andrà nel ramo del client, una versione globale nel ramo di versione o nel ramo del client se vuoi prototipare in un primo momento, ecc.)
  4. Prova a fare affidamento sull'elemento di lavoro per tenere traccia delle caratteristiche sviluppate per sapere in quale ramo è implementato per facilitare l'unione tra i rami.

Mirare il ramo giusto per te dev è la cosa più cruciale, non devi necessariamente definire alcune regole rigide di "cosa fare in quale occasione", ma cerca di essere coerente.

Ho lavorato su un grande progetto di 10 anni, con più di 75 versioni e quello che abbiamo fatto è stato di solito:

  • importante versione successivo: creare un nuovo ramo da Main, dev All'interno
  • minore Successivo versione: dev nel ramo principale corrente, usa Etichette per contrassegnare ogni versione minore all'interno del tuo ramo.
  • Alcune funzioni funzionali complesse sono state sviluppate nel ramo del client che l'ha richiesto, quindi invertite nel ramo della versione quando siamo riusciti a "senza marchio".
  • Correzioni di bug nel ramo client, quindi riportate in altri rami quando necessario. (devi usare l'elemento di lavoro per quello o ti perderai facilmente).

E 'il mio prendere su questo, altri possono avere diverso punto di vista, ho contato molto sulla elemento di lavoro per la tracciabilità del codice, che ha aiutato molto per la consegna e la segnalazione di codice.

EDIT

Ok, aggiungere qualche pensiero/risposte circa rami:

in Software Configuration Management (SCM) si hanno due caratteristiche che consentono di per versionning: rami e le etichette. Ognuno non è migliore né peggiore rispetto agli altri, dipende da quello che ti serve:

  1. un'etichetta è usato per marcare un punto nel tempo, utilizzando un marchio, per poter essere poi in grado di tornare a quel punto se necessario.
  2. Un ramo viene utilizzato per "duplicare" il codice per poter lavorare su due versioni allo stesso tempo.

Quindi utilizzare i rami dipende solo da ciò che si desidera essere in grado di fare. Se devi lavorare una di tante versioni diverse (diciamo una per cliente) allo stesso tempo: non c'è altro modo di affrontarla che usare i rami.

Per limitare il numero di sportelli si deve decidere quello che sarà un nuovo ramo o quello che sarà contrassegnata da un'etichetta per: client versioni specifiche, versione principale, versione secondario, Service Pack, ecc

Utilizzando le filiali per le versioni client sembrano essere una scelta imprudente. L'utilizzo di un ramo per ciascuna versione principale potrebbe essere la scelta più difficile da eseguire. Se si sceglie di utilizzare solo un ramo per tutte le versioni principali, non si avrà la flessibilità di lavorare su diverse versioni principali contemporaneamente, ma il numero di filiali sarà il più basso possibile.

Infine, Jemery Thompson ha un buon punto quando dice che non tutto il codice deve essere dipendente dal client, ci sono alcune librerie (in genere quelle di livello più basso) che non dovrebbero essere personalizzate per client. Quello che facciamo di solito è usare una struttura ad albero separata (che non è per cliente) per le librerie di servizi Framework, cross-cutting e di basso livello. Quindi fare riferimento a questi progetti nei progetti di versione per client.

Il mio consiglio per voi è utilizzare Nuget per queste librerie e creare il pacchetto nuget per loro, poiché è il modo migliore per definire le dipendenze versionned. Definire un pacchetto Nuget è davvero semplice, così come configurare un server Nuget locale.

+0

Grazie per la risposta. Posso vedere la ramificazione ha un senso. Se non cambio lo spazio dei nomi per identificare la versione del client, ciò renderebbe molto più semplice. Per qualche ragione ho appena immaginato un nucleo da cui sono state ereditate le versioni su misura. Potrebbe benissimo essere che solo la ramificazione e la reintegrazione delle personalizzazioni nel nucleo abbia più senso. Mi preoccupavo solo che con 30 o 40 versioni (la maggior parte delle quali non erano così diverse) la ramificazione stava aggiungendo complessità. – GraemeMiller

+2

I rami non costano molto in TFS, quindi la tua unica preoccupazione è su come gestire la quantità in modo efficiente. – Nock

+1

+1 bel riassunto di una bella domanda –

Problemi correlati