Diskussion Electron + Web-Anwendung mit einer Codebase?

Serbroda

Neues Mitglied
Hallo zusammen,

ich arbeite gerade an einer Anwendung, die sowohl als Web-App, als auch als Desktop Anwendung verfügbar sein soll. Ich versuche möglichst wenig doppelten Code zu schreiben und dieselbe Codebasis zu haben.

Mein Tech-Stack sieht aktuell wie folgt aus:

GUI: Angular 9
Desktop Anwendung: Electron
Backend für Webapp: Nodejs mit Koa.js Server (Alternative zu express)

Für die Desktop Anwendung wird das Angular Kompilat in das Electron Projekt kopiert und dort direkt die index.html gerendert. Die Web-App gibt entsprechend die index.html über einen Endpunkt zurück.

Zum Beispiel Datenbankzugriffe werden auf Electron-Seite in dem Main Prozess ausgeführt und über IPC zwischen Angular und Electron ausgetauscht. Für die Web-App entsprechend über REST-Calls.

Das bedeutet, und damit komme ich bald zu meiner Frage, muss ich Angular-Seitig immer zwei Services bauen. Für Electron einen der, zum Beispiel Datenbankabfragen, über IPC kommuniziert, für die Web-App einen, der über REST kommuniziert. Also habe ich an diesen Stellen aktuellen immer doppelten Code.

Beispielhafter Code für Services, die Angular-Seitig den Datenbankzugriff regeln:

Angular Services:
export abstract class DBService {
    abstract insert(item: any): any;
    abstract update(item: any): any;
}

// Service, wenn Web-App
@Injectable()
export class DBWebappService extends DBService {
    constructor(private http: HttpClient) {
        super();
    }
    insert(item: any): any {
        this.http.post('...');
    }
    update(item: any): any {
        this.http.put('...');
    }
    //...
}

// Service, wenn Desktop Anwendung
@Injectable()
export class DBElectronService extends DBService {
    constructor(private electron: ElectronService) {
        super();
    }
    insert(item: any): any {
        return this.electron.ipcRenderer.sendSync('db-insert', item);
    }
    update(item: any): any {
        return this.electron.ipcRenderer.sendSync('db-update', item);
    }
    //...
}

Ich könnte, und das würde generell alles etwas vereinfachen, den Http-Server der Web-App auch aus der Electron Anwendung starten, hätte damit also auch einen Endpunkt, den ich aus Angular ansprechen kann. Somit bräuchte ich für alle Prozesse, die Angular-Seitig getriggert und Backend-Seitig ausgeführt werden müssen, nur noch einen anstatt zwei Services erstellen und zwar nur einen für REST.

Meine Frage jetzt ist: Findet ihr es legitim, dass eine Desktop-Anwendung einen HTTP-Server startet und sich selbst dagegen connected und wenn die Anwendung geschlossen wird diesen wieder stoppt? Ich weiß nicht, ob das ggf. Antipattern ist, aber ich glaube pgadmin von PostgreSQL macht das ähnlich.

Ich hoffe meine Beschreibung wird klar und danke euch im Voraus!

Gruß
Danny
 
Zuletzt bearbeitet von einem Moderator:
Wenn ich das richtig verstanden hab, dann sehe ich da kein Problem damit. Du verringerst die Chance auf Bugs bei Updates.

Ganz viele Anwendungen starten lokale Webserver und sprechen dann mit sich selbst. Ob du jetzt IPC über ein eigenes Protokoll oder über Websocket mit REST machst, ist dabei ja nur ein Unterschied im Transportweg. Die App agiert sowieso schon so, als sei sie in einem verteilten System.

Musst nur dran denken, die REST-Aufrufe sicherheitstechnisch so zu behandeln, als kämen sie von einem Unbekannten und nicht von deiner App. Aber ich nehme mal an, du hast da eh schon Validierung drin. Vielleicht noch Token Authentifizierung, damit nur die Anwendung die Schnittstelle benutzen darf oder sowas.
 
Ich würde hier versuchen, die Kommunikation mit dem Backend in einen eigenen Service zu abstrahieren. Das setzt nur voraus, dass du im Node-Server Endpoints mit den gleichen Namen wie in der IPC-Kommunikation verwendest ‒ außer du möchtest eine Übersetzungs-Map erstellen.

D. h. du erstellst eine Funktion, die anstelle der anderen Services mit Electron bzw. Node kommuniziert. In deinen Services rufst du statt this.electron.ipcRenderer und this.http die Zwischenfunktion auf und übergibst ihr alle notwendigen Infos (Endpoint-Name und notwendige Parameter und Daten dafür). Dann brauchst du die Services nur noch einmal.

Für die Fallunterscheidung gibt es auch zwei Möglichkeiten:

- Du sorgst im Build-Prozess dafür, dass die richtige Funktion ausgeliefert wird
- Du machst die Unterscheidung im Code per if. Ich sehe, dass du per this.electron.ipcRenderer ipc nutzen kannst. Im Browser dürfte this.electron dann nicht existieren. Das dürfte als Bedingung fürs if genügen.

EDIT:
Wenn du den Server nur in Verbindung mit der Anwendung verwendest, würd ich mir persönlich keine Gedanken darüber machen, dass der Server nicht RESTful aussieht. Der Einfachheit halber würde ich alles über POST abwickeln und eine URL identisch zu den ipc-Namen verwenden.

Du hast hier eine spezielle Anforderung. Auch gibt es neben REST alternativen wie z. B. GraphQL, die auch POST-Requests dafür zweckentfremden.
 
Zuletzt bearbeitet:
Zurück
Oben Unten