"Soooviele Assemblies???" ist der ungläubige Ausruf, den ich oft höre, wenn ich ich über Komponentenorientierung spreche. Denn damit komponentenorientierte Entwicklung wirklich ihre Vorteile ausspielen kann, ist es nötig, Software nicht nur logisch in Komponenten zu zerlegen, sondern die auch noch physisch getrennt zu implementieren. Das bedeutet, jeder Komponentenkontrakt und jede Komponentenimplementation wird zu einer eigenen Assembly. Die Zahl der Assemblies einer Anwendung ist also immer mindestens doppelt so groß wie die ihrer Komponenten.
Das hört sich viel an. Oder zumindest ungewohnt. Schon bei 3 Komponenten kommen 5 oder 6 Assemblies zusammen, wie die nebenstehenden Beispielanwendung zeigt. Und größere Anwendungen bestehen schonmal aus 50 oder 100 Komponenten und damit 100 oder 200 Assemblies.
Einen "materiellen" Nachteil hat das jedoch nicht. Der .NET Framework kommt damit ganz gut zurecht. Nur Visual Studio nicht. Es macht keine Freude, in einer Visual Studio Projektmappe mehr als vielleicht 10 Projekte zu verwalten. Aber das soll man ja auch nicht. (Insofern entspricht die Abbildung links nicht dem Ideal; ich habe die Projekte nur für eine einfachere Darstellung so zusammengefasst.)
Echte Komponentenorientierung bedeutet vielmehr, dass alle Komponenten in je eigenen Projektmappen entwickelt werden. Wenn Sie an einer Komponente arbeiten sollen Sie nicht abgelenkt werden durch Implementationsdetails anderer Komponenten, die in derselben Projektmappe liegen.
Doch auch wenn es keinen "materiellen" Nachteil hunderter Assemblies für eine Anwendung gibt, verstehe ich, dass sie dazu angetan ist, den Überblick zu verlieren. Vor allem wollen Sie womöglich dem Kunden gegenüber solche Detailfülle nicht offenlegen. Die Abbildung rechts zeigt, wie ich die Beispielanwendung eigentlich ausliefern müsste: als "Sack von Assemblies". Das ist zwar einfach, aber mag sich eben zumindest merkwürdig anfühlen.
Krücke ILmerge
Nun habe ich aber noch nie behauptet, dass die Zahl der Assemblies zur Entwicklungszeit der zur Zeit der Auslieferung gleich sein müsse. Immer schon habe ich auf die Möglichkeit hingewiesen, sie mit Microsofts ILmerge zusammenzufassen. Aus den 7 zur Anwendung gehörenden Assemblies (inkl. DI Framework Infrastruktur) rechts im Bild könnte mit ILmerge ganz leicht eine werden.
Sie anders zusammenfassen, wäre allerdings nicht unproblematisch. Zum Beispiel könnten Sie nicht nur alle Kontrakt-Assemblies vereinigen, weil Sie damit die Referenzen darauf in den Komponentenimplementationen invalieren würden. Die beziehen sich ja auch konkrete Assemblynamen, die es dann nicht mehr gäbe.
ILmerge funktioniert zwar grundsätzlich. Ist aber letztlich nur eine Krücke, weil es nicht genügend Freiheitsgrade bietet. Außerdem mag es nicht jedem schmecken, dass ILmerge den IL-Code "anfasst". Zugegebenermaßen ein Vorgang, bei dem mehr falsch laufen könnte, als wenn man Assemblies, so wie sie sind, irgendwohin kopiert.
NETZ to the rescue
Aber diese Zeit der Krückenlösung ist nun vorbei! Durch einen News-Schnippsel in einer Entwicklerzeitschrift bin ich auf das Open Source Tool NETZ gestoßen. Das ist echt cool für die komponentenorientierte Entwicklung! Und es ist auch noch eines aus deutschen Landen - ein seltener Umstand bei Softwareentwicklungstools.
Was bietet NETZ? Zusammenfassung und Kompression von Assemblies. Mit NETZ können Sie Assemblies wie mit ILmerge zusammenfassen, allerdings ohne Eingriff in den IL-Code. NETZ verpackt die Assemblies einfach nur als Ressourcen in eine von ihm generierte.
Für die Komponentenorientierung unwichtig, aber für Sie dennoch vielleicht interessant, komprimiert NETZ die Assemblies während der Zusammenfassung auch noch. So kann der in den Ressourcen steckende IL-Code nicht mehr so einfach decompiliert werden. Ohne Obfuscation erhalten Sie also einen ersten Schutz gegen beiläufige Ausspähung von Implementationsdetails.
Die obige Beispielanwendung in eine Assembly zusammenzufassen, ist mit NETZ ganz einfach. Eine Kommandozeile wie diese reicht (sie ist nur der Lesbarkeit wegen hier in mehrere Zeilen geteilt):
netz -z -s
frontend.exe
-d logic.dll dataaccess.dll
contracts.logic.dll contracts.dataaccess.dll
Microsoft.Practices.Unity.dll Microsoft.Practices.ObjectBuilder2.dll
Mit ihr verpackt NETZ alle Assemblies in eine neue Assembly (-s), die auch eine DLL zur Dekompression enthält (-z). Alle Assemblies können auch dynamisch geladen werden (-d) - das ist für die Nutzung eines DI Frameworks nützlich sein mag. Das Resultat: eine Assembly mit Namen frontend.exe und einer Struktur wie in der nachstehenden Abbildung. Der Start-Code wurde komplett durch NETZ generiert (Methoden auf der linken Seite), die Anwendungsassemblies sind zu Ressourcen geworden (rechts). Der NETZ-Code sorft dafür, dass beim Laden einer Assembly, die die CLR nicht selbst findet, die Ressourcen herangezogen werden.
Schon bis hierhin bietet NETZ also mehr als ILmerge. Nicht nur Zusammenfassung von Assemblies, sondern gleichzeitig Kompression - und das ohne Eingriff in den Assembly-IL-Code.
NETZ kann aber noch mehr! NETZ kann die Assemblies auch in externe Ressourcen verpacken. So können Sie sie beliebig gruppieren. Auf die Referenzen hat das keinen Einfluss. Bilden Sie also beliebige Gruppen von Assemblies. Die Beispielanwendung habe ich z.B. einmal so zusammengefasst wie rechts zu sehen. In frontend.exe steckt die ursprüngliche EXE mit den Microsoft Unity DI Infrastruktur-Assemblies. Die contracts-Ressourcen enthalten die Kontrakt-Assemblies, die components-Ressource die Implementationen. Ganz einfach ist damit einem Batch wie diesem:
netz -z -s frontend.exe Microsoft.Practices.Unity.dll Microsoft.Practices.ObjectBuilder2.dll
netz -xr contracts contracts.logic.dll contracts.dataaccess.dll
netz -xr components dataaccess.dll logic.dll
move *-netz.resources frontend.exe.netz
Der -xr Switch weist NETZ an, die Assemblies nicht zu einer EXE, sondern zu einer externen Ressource zusammenzufassen. Der NETZ-Start-Code sucht in solchen Ressourcendateien im selben Verzeichnis sogar zuerst nach Assemblies.
Lassen Sie sich durch 50 oder 100 Assemblies nicht in Bockshorn jagen. Fassen Sie sie für´s Deployment mit NETZ einfach so zusammen, wie Sie es möchten: alle zu einer großen Assembly oder Kontrakte und Implementationen getrennt oder Kontrakte zusammen mit ihren Implementationen oder alle Assemblies einer Kategorie (z.B. alle Domänenlogik-Assemblies getrennt von allen Aspekt-Assemblies). Mit NETZ ist das alles sehr einfach möglich. Und kostenlos dazu! Mein herzlicher Dank gilt Vasian Cepa, dem Entwickler! (Der ist übrigens sehr bemüht; innerhalb eines Tages hatte er eine Verbesserung eingebaut, auf die ich ihn angesprochen hatte.)
Ich arbeite jetzt nicht mehr ohne NETZ in meinen komponentenorientierten Projekten.
2 Kommentare:
Hey Ralf
danke für den tollen Tipp!
bisher habe ich immer mit Finalbuilder + ILMerge gearbeitet und das ging auch immer ohne probleme
Werte NETZ auf jedenfall austesten!
Allerdingsverstehe ich auch nicht das problem dass man beim Kunden viele Assemblies hat.
Es dreht sich doch allenfalls um psychologie oder ?
Einzelne Assemblies bieten auch eine sehr gute autauschbarkeit und die Möglichkeit für gezielte Updates einzelner Komponenten (z.bsp. bei Bugfixes).
da muss ich Boas irgendwie Recht geben. Was ist das Problem daran beim Kunden 200 Dll's liegen zu haben? Ich kann ggf. besser austauschen (okay kann ich bei einer Datei auch) Ist einfach ne gewöhnungsbedürftige Sache mit den ganzen Dll's zu hantieren aber ansonsten kein Nachteil.
Bei dieser ganzen zusammenfasserei: funktioniert das auch mit verschiedenen Sprachen. Dort liegen ja die Resource.dll's in einzelnen Unterordner(de-DE oder en-US) ist das nen Problem oder funzed das?
Howard
Kommentar veröffentlichen
Hinweis: Nur ein Mitglied dieses Blogs kann Kommentare posten.