Frage an dependency-injection, tfs, architecture, asp.net-mvc-3, visual-studio-2010 – Was ist eine gute Lösungsstruktur, um eine einfache kundenspezifische Anpassung eines Produkts zu ermöglichen?

10

Ich bin auf der Suche nach Ratschlägen für die einfache Anpassung und Erweiterung eines Kernprodukts auf Kundenbasis. Ich weiß, dass es wahrscheinlich eine zu große Frage ist. Wir müssen uns jedoch einige Ideen einfallen lassen, da dies jahrelang Probleme bereiten kann, wenn wir das Setup falsch verstehen. Ich habe nicht viel Erfahrung in der Anpassung und Erweiterung bestehender Produkte.

Wir haben ein Kernprodukt, das wir normalerweise auf Kundenbasis maßschneidern. Wir haben kürzlich das Produkt in C # 4 mit einem MVC3-Frontend umgeschrieben. Wir haben überarbeitet und haben jetzt 3 Projekte, aus denen die Lösung besteht:

Kerndomänenprojekt (Namespace - Projektname.Domäne. *) - bestehend aus Domänenmodellen (zur Verwendung durch EF), Domänenservice-Schnittstellen usw. (Repository-Schnittstellen)Domäneninfrastrukturprojekt (Namespace -Projektname.Infrastruktur. *) - Implementiert den Domänenservice-EF-Kontext, die Repository-Implementierung, Schnittstellenimplementierungen für das Hochladen / Herunterladen von Dateien usw.MVC3 (Namespace - Projektname.web. *) - Projekt, das aus Controllern, Ansichtsmodellen, CSS, Inhalten, Skripten usw. besteht. Es verfügt auch über eine IOC (Ninject) -Handhabungs-DI für das Projekt.

Diese Lösung funktioniert problemlos als eigenständiges Produkt. Unser Problem ist es, das Produkt auf Kundenbasis zu erweitern und anzupassen. Unsere Kunden möchten in der Regel die Kernproduktversion sehr schnell (in der Regel innerhalb weniger Tage nach Vertragsunterzeichnung) mit Marken-CSS und -Styling erhalten. 70% der Kunden möchten dann jedoch, dass Anpassungen die Funktionsweise ändern. Einige Anpassungen sind klein, z. B. zusätzliche Eigenschaften für Domänenmodell, Ansichtsmodell und Ansicht usw. Andere sind bedeutender und erfordern völlig neue Domänenmodelle und Controller usw.

Einige Anpassungen scheinen für alle Kunden nützlich zu sein. Daher möchten wir sie in regelmäßigen Abständen von Anpassungen entfernen und sie dem Kern hinzufügen.

Wir speichern derzeit den Quellcode in TFS. Um ein Projekt zu starten, kopieren wir normalerweise die Quelle manuell in ein neues Teamprojekt. Ändern Sie den Namespace, um den Namen des Clients wiederzugeben, und passen Sie die Basisteile an. Stellen Sie sie dann in Azure bereit. Dies führt offensichtlich zu einer vollständig duplizierten Codebasis, und ich bin sicher, dass dies nicht der richtige Weg ist. Ich denke, wir sollten wahrscheinlich etwas haben, das die Kernfunktionen bereitstellt und bei Bedarf erweitert / überschreibt. Ich bin mir jedoch nicht sicher, wie ich vorgehen soll.

Daher suche ich nach Ratschlägen für die beste Projektkonfiguration, die Folgendes ermöglichen würde:

Schnelle Bereitstellung des Codes - so einfach kann ein neuer Client gestartet werden, um Branding / geringfügige Änderungen zu ermöglichenVermeiden Sie das Kopieren und Einfügen von CodeVerwenden Sie so viel DI wie möglich, um es locker gekoppelt zu haltenErmöglichen Sie die kundenspezifische Anpassung des CodesDie Möglichkeit, das Kernprodukt an einem einzigen Ort zu erweitern und alle Kunden mit dieser Funktionalität zu beauftragen, wenn wir die neueste Version des Kerns erhalten und erneut bereitstellen

Jede Hilfe / Beratung wird sehr geschätzt. Gerne fügen wir weitere Informationen hinzu, von denen jeder denkt, dass sie helfen werden.

Nachdem wir versucht hatten, für 5 Projekte pro Client zu verzweigen, gaben wir auf. Wir haben es jetzt als ein einzelnes Produkt geschrieben, das MEF und DI verwendet, um es abhängig von der Clientkonfiguration zusammenzuführen. Dies bedeutet auch, dass wir es auf einem einzelnen Webhost bereitstellen können und dass es besser skaliert werden kann. GraemeMiller
Siehe hierzu Mandantenfähigkeit, die die Situation ziemlich gut abdecktcodeofrob.com/entries/… GraemeMiller

Deine Antwort

2   die antwort
8

Ich kann nicht vollständig antworten, aber hier einige Ratschläge:

Kopieren Sie niemals Ihren Code, egal aus welchem ​​Grund.Benennen Sie den Namespace nicht um, um eine bestimmte Client-Version zu identifizieren. Nutzen Sie dazu die Filialen und die kontinuierliche Integration.Wählen Sie ein Verzweigungsmodell wie das folgende aus: eine Stammverzweigung mit dem Namen "Main", dann erstellen Sie eine Verzweigung aus "Main" pro Hauptversion Ihres Produkts und dann eine Verzweigung pro Client. Wenn Sie etwas entwickeln, legen Sie von Anfang an fest, in welchem ​​Zweig Sie entwickeln, je nachdem, was Sie tun (eine clientspezifische Funktion wird in den Client-Zweig eingefügt, eine globale Version in den Versionszweig oder einen Client-Zweig, wenn Sie Prototypen erstellen möchten es zuerst usw.)Verlassen Sie sich am besten auf Work Item, um die von Ihnen entwickelten Funktionen zu verfolgen und festzustellen, in welchem ​​Zweig es implementiert ist, um das Zusammenführen zwischen Zweigen zu vereinfachen.

Es ist das Wichtigste, den richtigen Zweig für Ihren Entwickler zu finden. Sie müssen nicht unbedingt einige strenge Regeln definieren, um festzulegen, was bei welcher Gelegenheit zu tun ist, sondern um konsequent zu sein.

Ich habe an einem großen 10 Jahre Projekt mit mehr als 75 Versionen gearbeitet und was wir normalerweise gemacht haben war:

Nächste Hauptversion: Erstellen Sie einen neuen Zweig von Main, dev InsideNächste Nebenversion: dev in der aktuellen Hauptniederlassung verwenden Sie Labels, um jede Nebenversion in Ihrer Niederlassung zu markieren.Einige komplexe Funktionsmerkmale wurden im Zweig des Clients entwickelt, der sie angefordert hatte, und dann umgekehrt in den Versionszweig integriert, als es uns gelang, die Marke zu entfernen.Fehlerbehebungen im Client-Zweig, die bei Bedarf in anderen Zweigen gemeldet werden. (Sie müssen dafür das Arbeitselement verwenden, da Sie sonst leicht verloren gehen).

Es ist meine Auffassung, dass andere möglicherweise eine andere Sichtweise haben. Ich habe mich in Bezug auf die Rückverfolgbarkeit des Codes sehr auf das Arbeitselement gestützt, was bei der Bereitstellung und Berichterstellung von Code sehr hilfreich war.

BEARBEITEN

Ok, ich füge ein paar Gedanken / Rückmeldungen zu Zweigen hinzu:

In Software Configuration Management (SCM) stehen Ihnen zwei Funktionen für die Versionsverwaltung zur Verfügung: Verzweigungen und Beschriftungen. Jedes ist weder besser noch schlechter als das andere, es hängt davon ab, was Sie brauchen:

Ein Label wird verwendet, um einen Zeitpunkt mit einem Label zu markieren, damit Sie später bei Bedarf zu diesem Punkt zurückkehren können.Eine Verzweigung wird verwendet, um Ihren Code zu "duplizieren", damit er bearbeitet werden kannzwei Versionen gleichzeitig.

Die Verwendung von Zweigen hängt also nur davon ab, was Sie tun möchten. Wenn Sie eine oder mehrere verschiedene Versionen gleichzeitig bearbeiten müssen (z. B. eine pro Client), gibt es keine andere Möglichkeit, damit umzugehen, als mit Zweigen umzugehen.

Um die Anzahl der Zweige zu begrenzen, müssen Sie entscheiden, was ein neuer Zweig sein soll oder was durch ein Etikett gekennzeichnet wird für: Kundenspezifische Versionen, Hauptversion, Nebenversion, Service Pack usw.

Die Verwendung von Zweigen für Client-Versionen scheint ein Kinderspiel zu sein. Die Verwendung eines Zweigs für jede Hauptversion ist möglicherweise die schwierigste Wahl für Sie. Wenn Sie nur einen Zweig für alle Hauptversionen verwenden, haben Sie nicht die Flexibilität, gleichzeitig an verschiedenen Hauptversionen zu arbeiten, aber die Anzahl der Zweige ist so gering wie möglich.

Schließlich hat Jemery Thompson einen guten Punkt, als er sagt, dass nicht Ihr gesamter Code clientabhängig sein sollte, es gibt einige Bibliotheken (normalerweise die untersten), die nicht pro Client angepasst werden sollten. In der Regel verwenden wir einen separaten Zweigbaum (nicht pro Client) für Framework-übergreifende Servicebibliotheken auf niedriger Ebene. Verweisen Sie dann auf diese Projekte in den Projekten pro Clientversion.

Mein Rat für Sie ist, Nuget für diese Bibliotheken zu verwenden und ein Nuget-Paket für sie zu erstellen, da dies der beste Weg ist, versionierte Abhängigkeiten zu definieren. Das Definieren eines Nuget-Pakets ist ebenso einfach wie das Einrichten eines lokalen Nuget-Servers.

Viel Glück. Versuchen Sie, sich nach der Implementierung des Versionsverwaltungsteils auf das Arbeitselement zu verlassen. Die versionierten typisierten Links von TFS 2010 sind großartig! Nock
+1 nette Zusammenfassung zu einer schönen Frage jim tollan
Filialen kosten in TFS nicht so viel. Sie müssen sich also nur darum kümmern, wie Sie die Menge effizient verwalten können. Nock
Wirklich zu schätzen die Ergänzung. Dies ist der Ansatz, den wir meiner Meinung nach verfolgen werden. Möglicherweise möchten wir auch so viel wie möglich modularisieren. Nuget-Pakete für einige der Funktionen könnten ebenfalls ein guter Ansatz sein. Wollte das für einige unserer Hilfsfunktionen tun, die von Lösungen gemeinsam genutzt werden. GraemeMiller
Vielen Dank für Ihre Antwort. Ich kann sehen, dass Verzweigungen Sinn machen. Wenn ich den Namespace nicht ändere, um die Client-Version zu identifizieren, wird dies viel einfacher. Aus irgendeinem Grund habe ich mir gerade einen Kern vorgestellt, von dem maßgeschneiderte Versionen geerbt wurden. Es könnte durchaus sinnvoll sein, Anpassungen nur zu verzweigen und wieder in den Kern zu integrieren. Ich habe mir nur Sorgen gemacht, dass bei 30 oder 40 Versionen (von denen die meisten nicht so unterschiedlich sind) die Verzweigung die Komplexität erhöht. GraemeMiller
4

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

+1 Gute Frage, es ist eher eine Geschäftsentscheidung, die Sie treffen müssen:

Möchte ich eine saubere Codebasis, in der die Wartung einfach ist und die Funktionen und Fehlerbehebungen schnell für alle unsere Kunden bereitgestellt werden?

oder möchte ich eine Fülle von Instanzen einer Codebasis aufteilen, jede mit winzigen Anpassungen, die schwierig sind(BEARBEITEN: es sei denn, Sie sind ein ALM-MVP, der die Marke aufheben kann) zu einem Kofferraum verschmelzen.

Ich bin mit fast allem einverstanden, was @Nockawa erwähnt, außer dass IMHO Ihre Code-Architektur nicht durch Verzweigungen ersetzen kann.

Verwenden Sie auf jeden Fall eine Zweig- / Trunk-Strategie, aber wie Sie bereits erwähnt haben, ist dies bei zu vielen Zweigen schwierigerquickly Roll-out von Funktionen für die gesamte Site und Verhinderung der projektweiten kontinuierlichen Integration. Wenn Sie das Kopieren / Einfügen verhindern möchten, begrenzen Sie die Anzahl der Zweige.

In Bezug auf eine Codierungslösung ist hier, was ich glaube, dass Sie suchen:

Module / Plug-Ins, Interfaces und DI sind genau richtig!Ableiten benutzerdefinierter Klassen von Basisklassen (Erweiterung der DSL pro Kunde, Assembly.Load ())Benutzerdefinierte Berichtslösung (anstelle neuer Seiten können viele benutzerdefinierte Anforderungen Berichte sein)Seiten mit Tabellenkalkulationen (hehe ich weiß - aber komischerweise funktioniert es!)

Gute Beispiele für den Modul- / Plugin-Punkt sind CMS wieDotNetNuke oder Kentico. Andere Ideen könnten durch einen Blick auf die Add-In-Architektur von Facebook, Plugins für die Audio- und Videobearbeitung, 3D-Modellierungs-Apps (wie 3DMax) und Spiele gewonnen werden, mit denen Sie Ihre eigenen Levels erstellen können.

Die ideale Lösung wäre eine Admin-App, mit der Sie Ihre Module (DLLs) auswählen, das CSS (Skin) anpassen, das dB skripten und die Lösung automatisch auf Azure bereitstellen können. Um dieses Ziel zu erreichen, ist das Plugin so viel sinnvoller, dass die Codebasis nicht aufgeteilt wird. Auch wenn ein Modul erweitert wird, können Sie es für alle Ihre Kunden bereitstellen.

Mit Benutzersteuerelementen, abgeleiteten Klassen und Funktionsüberschreibungen können Sie problemlos kleine Anpassungen vornehmen, z. B. zusätzliche Eigenschaften für Domänenmodell, Ansichtsmodell und Ansicht usw.

Tun Sie es wirklich generisch, wenn ein Kunde sagt, ich möchte eine Beschriftung erstellen, die das Alter aller Benutzer im System angibt und eine Funktion aufruftint SumOfField(string dBFieldName, string whereClause) und dann für diesen Kundenstandort ein Etikett haben, das an die Funktion bindet. Angenommen, ein anderer Kunde möchte, dass eine Funktion die Anzahl der Produktkäufe pro Kunde zählt. Sie können sie erneut verwenden: SumOfField ("product.itemCount", "CustomerID = 1").

Wesentlichere Änderungen, die völlig neue Domänenmodelle und Controller usw. erfordern, passen in die Plug-In-Architektur. Ein Beispiel könnte sein, dass ein Kunde ein zweites Adressfeld benötigt. Sie würden Ihr aktuelles Adress-Benutzersteuerelement so anpassen, dass es ein Plug-In für eine beliebige Seite ist. Sie müssten festlegen, welche dB-Tabelle und Felder die Schnittstelle für CRUD-Operationen implementieren können .

Wenn die Funktionalität pro Client in 30-40 Filialen angepasst wird, wird die Wartbarkeit so schwierig, dass ich das Gefühl habe, dass Sie sie nicht (einfach) zusammenführen können. Wenn es eine Chance gibt, dass dies wirklich groß wird, möchten Sie nicht 275 Filialen verwalten. Wenn es jedoch so spezialisiert ist, dass Sie für jeden Client die Benutzerkontrollebene aufrufen müssen und "Benutzer ihre eigenen Seiten nicht entwerfen können", ist die Verzweigungsstrategie von Nockawa für das Front-End durchaus vernünftig.

Nach 5 Kunden mit dem verzweigten Ansatz gaben wir auf. Es war ein Albtraum. Unser Kernprodukt veränderte sich ständig und erforderte ständige Zusammenschlüsse. Wir implementieren einen modularen Ansatz mit DI und auch mit MEF. Wir stellen es als einzelnes Produkt bereit und lassen die Konfiguration darüber informieren, welche Module / Anpassungen enthalten sind. Es war viel einfacher. Benötigt nur ein bisschen mehr Gedanken über das Design. GraemeMiller
Gute Antwort auch. Ich werde beide Ansätze verfolgen, denke ich. Ich bin auf der Suche nach Inspiration für die Modularisierung von Anwendungen bei Orchard. Ich werde noch eine Frage dazu schreiben und wahrscheinlich ein Kopfgeld darauf ausgeben. Zunächst konzentriere ich mich jedoch auf die Verzweigung, da dies der einfachste Einstieg ist, da nur wenige Clients auf die neue Codebasis umziehen. Sobald ich sehe, wo die Abweichungen auftreten, werde ich versuchen, diese Teile in Module umzuwandeln und sie dann zu verzweigen usw. GraemeMiller

Verwandte Fragen