16. Jun 2025
Lesedauer 13 Min.
Bezwinger der Produktvarianz
Mit Modulithen zu strukturierter Produktentwicklung
Welche Herausforderungen ergeben sich im Kontext von cyber-physischen Produkten, und wie kann man diesen mit dem Architekturmuster des Modulithen begegnen?

Oft wird in der Entwicklercommunity der Eindruck erweckt, dass Software ausschließlich in Form von Apps, Webseiten oder cloudbasierten Services existiert. Die Realität sieht jedoch anders aus: Zahlreiche Branchen setzen individuell angepasste Softwarelösungen ein, die darüber hinaus als eigenständige Produkte vertrieben werden. Gerade in Bereichen wie der Fertigungsindustrie, der Medizintechnik oder bei spezialisierten Expertensystemen spielt diese Art von Software eine entscheidende Rolle, da sie das finale Produkt nur in Form einer Symbiose mit entsprechender Spezialhardware darstellt.Dieser Artikel erläutert sowohl die Herausforderungen, die sich in diesem speziellen Zweig der Softwareentwicklung ergeben, als auch die damit verbundenen Lösungsansätze.
Begriffe des Software Product Line Engineering
Der ISO-Standard 26550 [4] aus der Reihe zum Software und Systems Engineering definiert verschiedene Begriffe des Product Line Engineering. Nachfolgend werden diejenigen kurz erklärt, die auch im Kontext dieses Dokuments verwendet werden.
SCADA und cyber-physische Systeme
Der zuvor benannte Spezialbereich der Produktentwicklung findet sich insbesondere im Kontext cyber-physischer Systeme. Salopp beschrieben, ist dies das Hardware-Pendant zu Microservice-Architekturen. In diesen Systemen gibt es demnach viele einzelne Komponenten, die für sich genommen eine eigene Intelligenz aufweisen, aber ihre eigentliche Funktionalität erst im Verbund entsprechender Steuerungs- sowie Auswertungssoftware und einer Art virtuellen Netzwerks erhalten. Zu diesen Komponenten können Sensoren zur Datenaufnahme, aber auch Aktoren zur Steuerung gehören.Moderne Autos sind hier ein gutes Beispiel. Ursprünglich wurden darin nur Werte wie die Geschwindigkeit oder der Füllstand des Tanks gemessen. Mittlerweile sind sie rollende Computer, die sich teils selbst steuern. Einfachere Varianten können aber auch Geld- oder Ticketautomaten sein, die meist mit Geldzähler, Kartenterminals und Thermodruckern ausgestattet sind, welche wiederum von einem Zentralrechner gesteuert werden und nicht selten ihre Daten über das Internet in weitere Computersysteme übertragen. Wann genau man es mit einem cyber-physischen System oder einfach nur einem Haufen Hardware nebst passender Firmware zu tun hat, ist dabei nicht immer offensichtlich. Ein wichtiger Entscheidungsfaktor ist aber der Grad der Konnektivität, siehe [1].Warum sollte man sich nun in einem Fachmagazin zur Softwareentwicklung mit einem so hardwarenahen Thema beschäftigen? Ist das nicht eher etwas für Embedded-Entwickler? Tatsächlich gibt es dafür diverse Gründe. Zum einen ist Deutschland nach wie vor ein Land, das sehr stark von Ingenieurwissenschaften außerhalb der Informatik geprägt ist. Viele Medizin- und Maschinenbauunternehmen sehen sich dabei mit der Herausforderung konfrontiert, dass ihre Produkte zunehmend „smart“ werden müssen, um am Markt bestehen zu können. Zu dieser „Smartheit“ gehört dann auch, dass sie untereinander kommunizieren können und ihre Daten für fortschrittliche Analysen genutzt werden. Geht man an diese Herausforderung ausschließlich mit der üblichen Denke eines Softwareentwicklers heran, sieht man sich schnell mit unerwarteten Schwierigkeiten konfrontiert. Dies betrifft insbesondere die langfristige Wartung und den Support unterschiedlicher Produktgenerationen. Über viele Jahre hinweg kann in einer solchen Umgebung ein komplexes Geflecht aus Anforderungen und Entwicklungsmethoden entstehen, das sich ohne strukturierte Planung nur schwer beherrschen lässt.Ein weiterer Grund für einen solchen Artikel in der dotnetpro ist das eigentliche Ökosystem. Bei näherer Betrachtung zeigt sich, dass bei vielen der angesprochenen Anwendungsfälle neben reinen Embedded-Systemen auch (Visual) C++ und C# zum Einsatz kommen. Besonders hervorzuheben sind dabei Supervisory Control and Data Acquisition Systems (SCADA), bei denen es sich um spezialisierte Systeme zur Überwachung und Steuerung industrieller Prozesse handelt. Sie werden in der Praxis sehr häufig auf Basis von .NET und C# umgesetzt. Daher betrachten wir nachfolgend zunächst diese besondere Welt abseits der akademisch ausgetretenen Pfade der Softwareentwicklung, widmen uns Vorgehensweisen zur Definition von Produktstrategien und zeigen, wie diese mit der passenden Architektur umgesetzt werden können.Systemdenken
Ein wesentlicher Unterschied in der Entwicklung von cyber-physischen Systemen im Vergleich zur reinen Softwareentwicklung besteht darin, das gesamte System verstehen, planen und umsetzen zu müssen (Bild 1). Dies erfordert beispielsweise eine enge Abstimmung zwischen Hard- und Softwareteams. Das Systemdesign beschreibt dabei die Gesamtarchitektur, die einzelnen Module, Schnittstellen und Datenstrukturen sowie deren spezifische Anforderungen. Dies ist nicht unähnlich zum Softwareentwurf, betrifft aber eben die komplette Kombination aus Software und Hardware. Dadurch erhalten etwa Anforderungen bezüglich Echtzeitfähigkeit oder Betriebssicherheit eine bedeutendere Rolle, als ihnen oft in reinen Softwareprojekten zugestanden wird. Um Missverständnisse und Fehler zu vermeiden, benötigt man somit ein gesamtheitliches Denken und interdisziplinäres Verständnis.
Zusammenhänge des Systemdesigns verdeutlicht (Bild 1)
Autor
Zu diesem Denken gehört dann beispielsweise auch, dass man eventuell nicht alle Softwarekomponenten in einer Hochsprache wie C# umsetzen sollte. Gerade wenn es um Echtzeitfähigkeit oder Betriebssicherheit geht, wird Logik daher oft eher in speicherprogrammierbare Steuerungen (SPS), programmierbare Schaltkreise (FPGAs) oder dergleichen verlagert. Damit verschieben sie sich aus Sicht eines klassischen Softwareentwicklers aber aus der Software in Richtung Hardware, während Physiker und Ingenieure die so entstandene Logik noch immer eher der Software zurechnen und daher die Verantwortung dafür auch bei den Softwareentwicklern sehen. Letztendlich sind sie alle Teil des Systems, und die Entscheidung, welche Systembestandteile wo umzusetzen sind, macht die zuvor genannte Zusammenarbeit über Fachgrenzen hinweg notwendig.Darüber hinaus unterscheiden sich auch Themen wie die Gewährleistung und Pflege von cyber-physischen Systemen stark von der reinen Software. Gerade bei Industrieanlagen können nicht beliebig oft und schnell neue Releases eingespielt werden, da Produktionsstopps möglichst zu vermeiden sind und Over-the-Air-Updates schon aus Sicherheitsgründen vom Kunden eher kritisch gesehen werden. Dabei weisen diese Anlagen auch nicht unbedingt die Leistungsfähigkeit auf, um Vorgehensweisen wie Containerisierung oder Virtualisierung umzusetzen und langfristig stabil zu halten. Hinzu kommt, dass diese Anlagen häufig in Kleinserien gefertigt werden und aus Komponenten von unterschiedlichen Hardwarelieferanten bestehen. Die Software muss daher auf eine gewisse Varianz der Hardwarekomponenten vorbereitet sein. Dies erfordert nicht nur eine sorgfältige Planung und Integration der Systeme, sondern auch eine nachhaltige Wartungsstrategie, um sie über lange Zeiträume hinweg funktionsfähig zu halten, unabhängig von der Vielfalt der eingesetzten Komponenten.Fassen wir also noch einmal die Herausforderungen übersichtlich zusammen:
- Die Software muss auf Varianzen in der Hardware und den Arbeitsabläufen vorbereitet sein.
- Sie muss daher sehr gut testbar und wandelbar sein.
- Sie muss ressourceneffizient gestaltet sein.
- Sie muss langfristig weiterentwickelt werden können.
- Sie unterliegt besonderen Freigabeprozessen.
- Sie unterliegt besonderen Anforderungen an ihre Stabilität, das Laufzeitverhalten und die Betriebssicherheit.
Produktlinien
Neben dem Systemdenken ergibt sich im Rahmen von Industriesoftware im weiteren Sinne und cyber-physischen Systemen im Speziellen eine Problematik, die bisher nicht genannt wurde. Diese Problematik entsteht aufgrund der Langlebigkeit solcher Produkte und der speziellen Marktgegebenheiten. Gemeint ist die Produktvarianz, die sich auf die unterschiedlichen Konfigurationen und Varianten eines Produkts bezieht. Diese Varianten entstehen im Lauf der Zeit durch Anpassungen an spezifische Kundenbedürfnisse, technische Weiterentwicklungen oder Änderungen in der verwendeten Hardware.Betreibt man beispielsweise einen Webshop und schreibt dafür entsprechende Software, hat man in der Regel eine Instanz des Gesamtsystems aktiv (Bild 2). Zugegeben, dank Replikation in der Cloud als Backup für Ausfälle und durch die Verteilung der Gesamtlogik auf verschiedene Dienste können es auch mehrere Instanzen sein. Von außen betrachtet gibt es jedoch im Grunde nur eine aktuell laufende Version des Webshops.
Herausforderungen von Produkten (Bild 2)
Autor
Bei Maschinensteuerungssoftware hingegen läuft eine Instanz der Software auf jeder Maschine. Dabei muss jede Softwareinstanz aktuell gehalten werden, selbst wenn diese Maschinen Komponenten unterschiedlicher Ausprägung enthalten, weil sie beispielsweise von unterschiedlichen Hardwarelieferanten stammen. Dies ist vergleichbar mit typischer Desktop-Software oder jeder beliebigen Handy-App. Der große Unterschied ist jedoch, dass bei Handy-Apps das jeweilige Betriebssystem automatische Mechanismen für Update, Deployment und Hardwareabstraktion besitzt. So sorgen Android und iOS dafür, dass unsere App-Updates auf die Geräte der Nutzer gelangen und man dort niemals direkt mit der Hardware spricht. Xamarin, MAUI und Co. bieten vereinheitlichte Schnittstellen als SDKs an, über die wir mit jeder beliebigen Kamera auf jedem beliebigen Telefon kommunizieren können, ohne zu wissen, von welchem Hersteller diese stammt oder welche Firmware darauf läuft.Im Kontext von cyber-physischen Systemen hat man diesen Luxus nicht. Zum einen werden dort meist Linux und Windows als Betriebssysteme eingesetzt, zum anderen handelt es sich bei der verwendeten Hardware oft um Spezialsysteme, die mehr tun, als nur ein Foto zu schießen oder ein Video aufzunehmen. Da wollen Laser vorgeheizt, Gase in eine Prozesskammer gelassen und Barcode-Lesegeräte angesteuert werden. Die Geräte sind zwar für die jeweiligen Produkte klar spezifiziert, können sich jedoch in ihrem API über Produktgenerationen hinweg ändern, zum Beispiel weil der Hersteller Anpassungen an seinen Hardwarekomponenten vorgenommen hat.Dazu kommt, dass sich auch unsere eigenen Produkte weiterentwickeln. Auch wir werden für technischen Fortschritt bezahlt, was dazu führt, dass die Arbeitsabläufe in den Maschinen komplexer werden und neue Produktvarianten sowie Produktlinien entstehen. Von uns als Softwareentwicklern wird erwartet, dass wir diesen Zoo an möglichen Kombinationen mit der Macht und Magie von Software bändigen. Dabei sollen wir möglichst viel bereits Geschaffenes wiederverwenden, um den „Return on Invest“ hoch und die „Time to Market“ gering zu halten. Schnell stellt sich die Frage: Wie schaffe ich diese Quadratur des Kreises, steigender Komplexität mit Kosteneinsparungen zu begegnen? Die Antwort darauf ist fast schon langweilig: Mit Modularität!
Warum Microservices nicht reichen
Eine viel diskutierte Form der Modularität sind Microservices. Dank Docker und Kubernetes zerlegt man die Gesamtlogik in viele kleine Dienste, die über fest definierte Schnittstellen entweder per REST, RPC oder einen Message Bus beziehungsweise Event Broker miteinander kommunizieren. Je nach Umgebung wird man jedoch bei einigen dieser Ansätze feststellen, dass sie mit einem völlig anderen Anwendungszweck entwickelt wurden. Dadurch sind Dinge, die im Web als besonders schnell gelten, eventuell nicht schnell genug, um beispielsweise auf einem einzigen Rechner im Millisekundentakt verlässlich Daten aus einem Videostream abzutasten, zu analysieren und zur weiteren Bearbeitung an andere Systembestandteile weiterzugeben. Auch das Thema Replikation von Daten kann bei einem naiven Ansatz von Containern zu einer mittelschweren Katastrophe führen, wenn ein zu klein geratener Steuerungsrechner Gigabytes an Daten zwischen den auf ihm laufenden Containern hin und her schaufeln muss. Den Container wegzulassen und einfach auf Webservices zu setzen, hilft ebenfalls nur bedingt, weil es Dinge gibt, die man in ihrer schieren Masse nicht einfach über ein virtuelles Netzwerk schieben kann.Das Kernproblem sind hierbei nicht die Container. Diese haben durchaus verschiedene Vorteile im Bereich des Deployments, der Ausfallsicherheit und der Skalierung. Es ist vielmehr ihre Granularität, die zu Schwierigkeiten führen kann. Wenn die Containerebene die einzige Möglichkeit ist, modular zu arbeiten, läuft man Gefahr, entweder zu große Dienste umzusetzen, die innerlich mehr den Monolithen gleichen, oder so viele Mini-Dienste zu schaffen, dass sich das betrachtete System dank seines Overheads selbst ausbremst.Betrachtet man diese Zusammenhänge, kann man recht eindeutige Anforderungen an eine Softwarelösung und ihre Architektur im beschriebenen Einsatzfeld definieren:- Sie sollte zur Laufzeit möglichst ressourcenschonend sein, um auch in Umgebungen mit geringerer CPU-Leistung oder Speicher ausgeführt werden zu können.
- Sie sollte eine Granularität erlauben, mit der die Besonderheiten der eigentlichen Fachdomäne und die unterschiedlichen Produktversionen möglichst gut abgedeckt werden können.
- Sie sollte langfristig möglichst leicht gepflegt werden können. Dazu gehört auch, dass Kernkomponenten möglichst wenig Abhängigkeiten aufweisen.
Die Architektur
Fügt man eine Modulstruktur einer typischen Client-Server-Architektur hinzu, ergibt sich eine mögliche Zusammenstellung wie in Bild 3. Dabei werden sowohl die Dienste als auch deren Klient intern noch einmal in fachgebundene Module aufgeteilt. Dies dürfte weniger verwundern, nutzen wir doch ohnehin regelmäßig DLLs innerhalb unserer Applikationen, um deren Bestandteile zu gruppieren. Der Unterschied bei Modulithen ist jedoch, dass die Definition eines Moduls über die einer DLL hinausgeht. Während es sich bei Letzterer nur um eine Datei handelt, besteht ein Modul üblicherweise aus mindestens zwei DLLs: einer für die öffentlichen Abstraktionen beziehungsweise Schnittstellen und einer für die eigentliche Implementierung. Dies ermöglicht es, die Implementierung sehr einfach über späte Bindung und einen guten IoC-Container auszutauschen, solange die Schnittstellen stabil bleiben. Im vorangegangenen Beispiel hätte man also eine DLL für die allgemeine Definition von Kamerasystemen und dann je Kamerahersteller eine eigene Implementierung dieser Schnittstellen.
Architektur eines einzelnen Produkts (Bild 3)
Autor
Damit könnte man beispielsweise ein Team einen Service erstellen lassen, mit dem Bilder von Kameras aufgenommen und direkt analysiert werden, um die Analysedaten dann in einer Benutzeroberfläche anzuzeigen. Dieser Service kann für jedes Produkt genutzt werden, das diese Grundfunktionalität benötigt. Je nach Produkt wird er aber mit unterschiedlichen Modulimplementierungen ausgestattet, basierend auf der vorliegenden Hardware und den notwendigen Analysen. Das API des Service bleibt hingegen wieder stabil, da es nur mit den Schnittstellen der Module arbeitet.Ob und wie die Module nun also zusammengefasst werden, ist ein Implementierungsdetail, das dadurch bestimmt wird, ob bestimmte Logiken möglichst nah beieinander liegen müssen, um Ressourcen zu sparen, oder ob die Bestandteile möglichst gegen Ausfallsicherheit abgesichert werden sollen beziehungsweise bestimmten Skalierungsanforderungen unterliegen. So wäre es beispielsweise auch möglich, nur einen Dienst und einen Rich-Client bereitzustellen oder einen Thin-Client umzusetzen, der die Bestandteile seiner Benutzerschnittstelle als Micro-UIs von den Diensten geliefert bekommt.Das zeigt, wie viel flexibler die Architekturen werden, indem man beide Architekturmuster miteinander kombiniert. Notwendig ist dafür aber eine gemeinsame Plattformstrategie.
Plattformstrategie
Laut Pohl et al. [3] lohnt sich eine gemeinsame Plattform ab etwa drei Produkten. Erst dann sind die Synergieeffekte so nachhaltig, dass sie die aufkommenden Entwicklungskosten einer Plattform rechtfertigen. Diese Plattformen umfassen in aller Regel neben standardisierten Entwicklungsmethoden und Prinzipien auch Komponenten für wiederkehrende Aufgaben sowie ein einheitliches Applikationsgerüst und einen Wissensspeicher. Dabei müssen sie möglichst unabhängig von einem konkreten Produkt entwickelt werden, da sonst zu viele Spezifika aus den Produkten in die Plattform übergehen und gegebenenfalls andere Produkte behindern. Bild 4 zeigt den Gesamtaufbau, der für eine Produktstrategie notwendig ist.
Von der Business-Strategie zum Produkt (Bild 4)
Autor
Diese Strategie darf nicht nur von rein technischen Aspekten getrieben werden, sondern muss sich auf Basis der Business-Strategie des Unternehmens ergeben. Dazu gehören Budgetvorgaben, aber auch Marktanalysen und eine Portfoliostrategie, welche aussagt, welche Produkte es gibt und wie diese sich langfristig entwickeln sollten. Als Softwareentwickler und -architekt hat man hier meist nicht allzu viele Mitbestimmungsrechte. Diese ergeben sich erst in der darauf basierenden strategischen Perspektive, die die Prinzipien, Methoden und Technologien festlegt, welche über alle Produkte hinweg gleich sein müssen und in einer Enterprise-Architektur münden.In der taktischen Sicht sind wir dann bei dem, wo auch Entwickler zum Einsatz kommen. Diese entwickeln gemeinsame Frameworks und Leitlinien, die in verschiedenen Produkten genutzt werden können. In vielen Unternehmen geschieht dies auch völlig automatisch; ohne den strategischen Unterbau fristen die so geschaffenen Helfer aber schnell ein Schattendasein, da sie nicht ausreichend finanziert sind und Entwickler zu wenig Zeit für ihre Pflege bekommen.Verbindet man alle bisher beschriebenen Sichten mit einer Domänenperspektive, kommt man zu den eigentlichen Softwaremodulen. Diese können dann wie zuvor beschrieben genutzt werden, um in unterschiedlichen Produkten wiederverwendet zu werden.Demzufolge ergeben sich zwei Arten von Plattformen: eine Basisplattform, wie sie in Unternehmen fast schon automatisch entsteht, sobald Entwickler ihre generellen Tools und Utilities zentral verwalten, und eine eigentliche Produktplattform, die sich wiederverwendbares Fachwissen zunutze macht, um diverse Produkte zu unterstützen.
Fazit
Die in diesem Artikel dargestellten Zusammenhänge sind sehr komplex und umfangreich. Nicht jedes Unternehmen wird sofort eine mehrstufige Produktstrategie mit passender Softwareplattform schaffen. Dies ist auch nicht zwangsläufig notwendig, insofern man von Beginn an in fachlich geschnittenen Modulen denkt. Wie man hierbei konkret vorgeht, findet sich im passenden Artikel der dotnetpro 2/2025 [2]. Sollte man dann in den Zwang geraten, mehrere Produkte zu unterstützen, können einige dieser Module und ihre Infrastruktur zu einer Plattform zusammengefasst und allen Produkten bereitgestellt werden. So ergibt sich ein fließender Übergang zum Product Line Engineering ohne umfangreiche Anfangsinvestition. Dies ist gerade im Kontext komplexer Hardware von Vorteil, da man nur so die Vielschichtigkeit der dort vorliegenden Varianzen in den Griff bekommen kann.Fussnoten
- Cyber-physisches System bei Wikipedia, http://www.dotnetpro.de/SL2506-07ModulithPraxis1
- Hendrik Lösch, Pragmatisch Richtung Microservices, dotnetpro 2/2025, Seite 48 ff., http://www.dotnetpro.de/A2502Modulithen
- Klaus Pohl, Günter Böckle, Frank van der Linden, Software Product Line Engineering: Foundations, Principles and Techniques, Springer, 2005, ISBN 978-3-540-24372-4,
- ISO-Standard 26550 auf OBP (Online-Datenbank der ISO), http://www.dotnetpro.de/SL2506-07ModulithPraxis2