Architektur einer modularen Dienstanwendung


11

Ich möchte eine neue Lösung entwickeln, die von Natur aus sehr modular ist, und möchte eine Struktur erstellen, die dieses Design unterstützt, um eine einfache zukünftige Erweiterung, eine klare Trennung von Bedenken, eine Lizenzierung nach Modulen usw. zu ermöglichen. Das meiste, was ich habe Im Internet finden Sie Informationen zu modularen oder zusammengesetzten Anwendungen, die sich auf die Benutzeroberfläche konzentrieren und sich auf Silverlight, WPF usw. konzentrieren. In meinem Fall entwickle ich eine WCF-Dienstanwendung, die von anderen Entwicklern verwendet wird, die an verschiedenen Benutzeroberflächenprojekten arbeiten.

Hintergrund

Das Ziel meines Projekts ist es, eine zentralisierte Quelle für Geschäfts- / Domänenlogik zu schaffen, um mehrere unserer Geschäftsanwendungen zu unterstützen, die derzeit Prozesse, Regeln usw. duplizieren. Auch wenn nicht alle Vorteile der Modularität erzielt werden, möchte ich die nutzen Gelegenheit, einen Rahmen zu schaffen, der die Teile nutzt, die wir nutzen können.

Ich habe das Design mit einem Blick auf die API begonnen, die von der Dienstanwendung bereitgestellt wird. Es ist klar, dass die Dienste nach den von mir in Betracht gezogenen modularen Linien getrennt werden können. Zum Beispiel werde ich Services von FinanceService, InventoryService, PersonnelService usw. haben, die meine Servicevorgänge gruppieren, um eine hohe Kohäsion in der API zu gewährleisten und gleichzeitig die Kopplung niedrig zu halten, sodass Clients nur die Services verwenden müssen, die für ihre Anwendung relevant sind.

Für mich ist es sinnvoll, dass ich dann für jeden dieser Dienste separate Module haben kann, z. B. MyApp.Finance, MyApp.Inventory, My.Personnel usw. Querschnittsthemen und gemeinsam genutzte Typen befinden sich in der gemeinsam genutzten MyApp-Assembly. Von hier aus werde ich ein wenig gefesselt.

(Oh, ich sollte erwähnen, dass ich einen IoC-Container für Dependency Injection verwenden werde, um die Anwendung lose gekoppelt zu halten. Ich werde nicht erwähnen, welcher, weil ich die Büchse der Pandora nicht öffnen möchte!)

In MyApp.ServiceHost erstelle ich eine Service-Host-Datei (.svc), die jedem Modul entspricht, z. B. FinanceService.svc. Der Service-Host benötigt den Namen des Service, der den Informationen in der Konfigurationsdatei entspricht, die die Schnittstelle enthält, die den Servicevertrag definiert. Die IoC-Konfiguration wird dann verwendet, um die konkrete Implementierung der zu verwendenden Schnittstelle abzubilden.

1. Sollte die Service-Schicht die API implementieren und an die Module delegieren oder sollten die Module in sich geschlossen sein (da sie alles enthalten, was mit diesem Modul zusammenhängt, einschließlich der Service-Implementierung)?

Eine Möglichkeit, das Problem anzugehen, besteht darin, ein "Modul" von MyApp.Services zu haben, das die Implementierung der Serviceverträge enthält. Jede Serviceklasse delegiert einfach an eine andere Klasse in dem entsprechenden Modul, das die Domänenlogik für die Operation enthält. Beispielsweise delegiert die FinanceService WCF-Klasse in MyApp.Services an eine andere Schnittstelle, die im Finanzmodul implementiert ist, um den Vorgang auszuführen. Dies würde es mir ermöglichen, eine dünne Servicefassade beizubehalten und die Implementierung in die Serviceimplementierung einzufügen und die Notwendigkeit zu beseitigen, dass sich die Module beispielsweise um WCF kümmern müssen.

Andererseits ist es vielleicht vorzuziehen, dass jedes Modul in sich geschlossen ist, da es die Schnittstelle und die Implementierung hat. Der Service-Host bezieht sich auf die im Modul enthaltene Servicevertragsschnittstelle, und der IoC ist so konfiguriert, dass auch die entsprechende Implementierung des Moduls verwendet wird. Dies bedeutet, dass das Hinzufügen eines neuen Moduls ohne Änderungen an der Serviceschicht durchgeführt werden kann, außer durch Hinzufügen einer neuen SVC-Datei und IoC-Konfigurationsinformationen.

Ich denke über die Auswirkungen nach, wenn ich von Standard-WCF zu einer RESTful-Service-Schnittstelle wechsle oder vielleicht zu RIA-Services oder so etwas gehe. Wenn jedes Modul die Implementierung des Servicevertrags enthält, muss ich in jedem Modul Änderungen vornehmen, wenn ich die Servicetechnologie oder den Serviceansatz ändere. Wenn die Fassade jedoch ein eigenes Modul ist, muss ich nur diesen Teil austauschen, um eine Änderung vorzunehmen. Die Module müssten dann einen anderen Satz von Verträgen (Schnittstellen) implementieren, die möglicherweise in der gemeinsam genutzten Assembly definiert sind.

2. Was ist der beste Weg, um Ressourcen zwischen Modulen und / oder Abhängigkeiten zwischen Modulen gemeinsam zu nutzen?

Nehmen Sie zum Beispiel eine Empfangsoperation. Auf den ersten Blick ist es sinnvoll, dass dies in das Inventarmodul aufgenommen wird, da der Wareneingang eine Inventarfunktion ist. Es gibt jedoch auch einen finanziellen Aspekt darin, dass wir eine Quittung erstellen und die Zahlung autorisieren müssen.

Einerseits würde ich erwarten, irgendeine Art von Domänenereignissen / Nachrichten zu verwenden, um den Vorgang zu kommunizieren. Das Inventarmodul löst ein GoodsReceivedEvent aus, das vom Finanzmodul verarbeitet wird, um den Beleg zu generieren und den Zahlungsprozess einzuleiten. Dies bedeutet jedoch, dass das Finanzmodul über die eingegangenen Inventargegenstände Bescheid wissen muss. Ich kann sie einfach anhand des Personalausweises angeben, aber was ist, wenn ich zusätzliche Informationen für die Quittung benötige, z. B. Name und / oder Beschreibung, Stückkosten usw.? Ist es sinnvoll, dass jedes Modul eine eigene Version eines Inventargegenstands hat, der auf die Anforderungen dieses Moduls zugeschnitten ist? In diesem Fall müsste das Finanzmodul beim Umgang mit dem GoodsReceivedEvent eine eigene Suche der Inventargegenstände durchführen.

...

Ich habe dieses Beispiel absichtlich verwendet, weil ich viel mit verschiedenen ERP-Systemen gearbeitet habe und weiß, dass sie mit dieser Art von Modularität ausgelegt sind - ich weiß nur nicht wie. Ich erwähne es oben auch nicht explizit, aber ich ziehe es vor, bei der Architektur der Lösung den Prinzipien von Domain Driven Design zu folgen und glaube, dass diese Art von Modularität genau in diesen Bereich passt.

Jede Hilfe, die meinen Kopf darum wickelt, wird sehr geschätzt.


1
Es tut uns leid. Ich kann in all dem Denken keine Frage finden. Was ist die Frage?
S.Lott

1
Suchen Sie nach den Fragezeichen. Es gibt mehrere Punkte, an denen ich Optionen anbiete, aber keine Lösung voraussetze, und einige spezifische Fragen, die den Prozess auf den richtigen Weg führen.
SonOfPirate

1
@ SonOfPirate: Entschuldigung. Ich hatte gehofft, Sie könnten sich die Zeit nehmen, die Fragen hervorzuheben oder hervorzuheben, damit andere Ihnen helfen können.
S.Lott

4
Ich stimme S.Lott zu. Es tut mir leid, aber das ist keine Frage, es ist ein Bewusstseinsstrom. Wir können Ihnen viel besser helfen, wenn Sie dies auf ein oder zwei fokussierte Fragen reduzieren und versuchen, die architektonischen Belange von den Implementierungsdetails zu trennen.
Aaronaught

1
@SonOfPirate: Bei "Architektur" geht es um Struktur und die Kommunikation dieser Struktur mit den anderen Entwicklern. Es beginnt mit der Beschreibung. Es muss ein Maß an Klarheit und Transparenz erreichen, das es anderen Entwicklern ermöglicht, es vollständig zu "verstehen" und bei allem, was sie tun, den architektonischen Gestaltungsprinzipien zu folgen.
S.Lott

Antworten:


2

Ist es sinnvoll, dass jedes Modul eine eigene Version eines Inventargegenstands hat, der auf die Anforderungen dieses Moduls zugeschnitten ist? In diesem Fall müsste das Finanzmodul beim Umgang mit dem GoodsReceivedEvent eine eigene Suche der Inventargegenstände durchführen. Wo ziehe ich die Grenze zwischen Modularität und der Notwendigkeit, Informationen auszutauschen?

Sie müssen immer Kompromisse eingehen. Das ist alles, was Sie zu Ihren Fragen wirklich sagen können. Es hat sich empfohlen, die Entitäten in einer separaten Assembly zu speichern, sodass viele Anwendungen sie in einer gemeinsam genutzten Bibliothek speichern. Das bedeutet aber, dass es keine (physischen) geschäftslogischen Grenzen gibt. Das Durchführen eigener Suchvorgänge bedeutet viel redundanten Code, den ich nur implementieren würde, wenn Sie in beiden Modulen spezielle Anforderungen haben. Aus architektonischer Sicht müssen Ihr Finanzmodul und Ihr Inventarmodul natürlich ohnehin eine Abhängigkeit aufweisen. Das Finanzmodul ist höchstwahrscheinlich vom Inventarmodul abhängig. Wenn die Anforderungen es Ihnen ermöglichen, ein Modul von einem anderen Modul abhängig zu machen, ist es in Ordnung, die Entitäten in den Modulen zu kapseln, zu denen sie am meisten gehören. Du' Ich muss ein gutes Gleichgewicht zwischen physischer Unterscheidung (gemeinsame Abhängigkeiten) und Wartung finden. Zu viele gemeinsame Abhängigkeiten können einen Alptraum bei der Wartung von Versionen verursachen. Bei einem ORM treten außerdem Probleme auf, wenn Sie vorhandenen Entitäten neue Beziehungen zuordnen oder diese aus Modulen erweitern möchten, die von dem Modul abhängen, das die Entität enthält.

Wenn Sie ORM nicht verwenden, denken Sie daran, dass wahrscheinlich alle Ihre Module ohnehin dieselbe Datenbank verwenden, wie dies häufig der Fall ist. Sie könnten das als Gemeinsamkeit nutzen.

Sie können die Daten auch einfach halten (z. B. in Bezug auf CSV / Ergebnismengen) und einen zentralen Messaging-Dienst verwenden, um Module, die sich bei ihr registriert haben, über neue Daten sowie einige Metainformationen zu informieren. Das bedeutet keine wirklichen Abhängigkeiten, sondern opfert Typensicherheit und Verträge und es kann Probleme mit fehlerhaften Daten geben. Ich denke, so viele große ERP-Systeme machen das.

Kurz gesagt, Sie müssen sich entscheiden, welche Art von Schnittstelle Sie möchten. Mehr Modularität führt zu weniger Verträgen und umgekehrt.

Ich würde wahrscheinlich eine gemeinsam genutzte Bibliothek für meine Datenobjekte und einen zentralen Nachrichtendienst mit einem Veröffentlichungs-Subscibe-Muster verwenden, das scheint mir die beste Lösung zu sein.


Stimmen Sie auch dem zu - Messaging + Pub / Sub und Shared Libraries für Dto-Verträge (internes Nuget Repo). Ich habe mich die gleichen Fragen wie @SonOfPirate gestellt, bis dieser Artikel es für mich relativierte
diegohb
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.