Entwurf einer
dienstorientierten
Plug-in-Architektur für
Webanwendungen
am Beispiel von
Bachelorarbeit
Jonas Thelemann
20.01.2022
Gliederung
- Projekt
- Entwurf
- Implementierung
- Vertiefung
Code
Dritter
Design
+
Kontext
2014
2016
2018
2020
2022
Gründung NAHhaft
Idee
Antragsphase
Gründung, Beta
Logistik-Marktplatz
nearbuy
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274511/nearbuy-dashboard.png)
Dashboard
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274527/nearbuy-company.png)
Betriebsprofil
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274512/nearbuy-marketplace.png)
Marktplatz
nearbuy
Frontend
React
Anforderungen
- 🔒 sichere Datenzugriffe
- 🕵 Nachvollziehbarkeit von Interaktionen
- ⛓ Sicherstellung der Kompatibilität
- 🎨 visuelle Konsistenz
- 🏪 Erstellung eines Plug-in-Markplatzes
- 🔂 Installation pro Konto oder Betrieb
- 🪝 mehrere Erweiterungspunkte
Entwurf
Entwurf
Entwurf
Entwurf
Netzwerkstruktur
Netzwerkstruktur
Flexible Verwendung konsistenten Designs
Webkomponenten
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274516/webcomponents-react-speed.png)
Flexible Verwendung konsistenten Designs
Webkomponenten
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274516/webcomponents-react-speed.png)
Stencil
Flexible Verwendung konsistenten Designs
Webkomponenten
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274516/webcomponents-react-speed.png)
Stencil
Bit
Flexible Verwendung konsistenten Designs
Webkomponenten
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274516/webcomponents-react-speed.png)
Stencil
Bit
Datenmodell eines Plug-ins
const nearbuyPluginMuesliIndex: NearbuyPluginOptions = {
id: "muesli-index",
name: {
de: "Muesli-Index", en: "Muesli Index"
},
description: {
en: "Calculates the 'Muesli index', representing the availability of ingredients
required for a yummy muesli. Requests for missing ingredients can be added
with one click."
},
version: packageJson.version,
versionNearbuyCompatibility: packageJson.peerDependencies.nearbuy,
url: new URL("http://localhost:3001"),
iconPngBase64: "iVBORw0KGgoAAAA...",
entryPoints: {
dashboard: [{
dashlet: [{
contentUrl: "https://nearbuy_muesli-index.jonas-thelemann.de/",
}],
}],
plugin: {
contentUrl: "http://localhost:3001/",
},
},
}
Implementierung
- 🔑 Autorisierung von API-Zugriffen durch Dritte
- 💻 Webkomponenten
- 📦 Plug-in-Installation
1/2 Konfiguration von Keycloak
2/2 Erweiterung der Anfragevalidierung im Backend
val userinfo = parseUserinfo(String(Base64.getDecoder().decode(xUserinfoEncoded)))
this.scopes.addAll(userinfo.scope.split(" "))
val annotationScope =
"${annotation.clazz.name.lowercase()}-${annotation.action.name.lowercase()}"
if (!(requestContext.securityContext as NearbuySecurityContext).hasScope(annotationScope)) {
abort(requestContext)
}
@Allowed(READ, COMPANY, "{companyId}")
Autorisierung von API-Zugriffen durch Dritte
<Button
onClick={(): void => this.props.
onClose()}
/>
<Button
onClick={(): void => this.saveOffer()}
color="primary"
variant="contained"
disabled={...}
/>
<NbButton
onNbButtonEvent={(): void =>
this.props.onClose()
}
secondary
/>
<NbButton
onNbButtonEvent={(): void =>
this.saveOffer()
}
disabled={...}
/>
React
Einbindung in Webanwendungen
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274509/nearbuy-buttons.png)
Buttons
Webkomponenten
import { applyPolyfills, defineCustomElements }
from "@dargmuesli/nearbuy-web-components/loader"
Vue.config.ignoredElements = [[/nb-\w+/]
applyPolyfills().then(() => {
defineCustomElements()
})
{
hydratePath: ’@dargmuesli/nearbuy-web-components/hydrate’,
lib: ’@dargmuesli/nearbuy-web-components’,
loaderPath: ’@dargmuesli/nearbuy-web-components/loader’,
prefix: ’nb-’,
},
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275287/Nuxt.png)
Vue
Nuxt
Einbindung in Webanwendungen
Webkomponenten
Webkomponenten
@Prop() secondary: boolean = false;
@Prop() disabled: boolean = false;
@Prop({ attribute: ’first-last’ }) name: Name = {
first: ’Max’,
last: ’Mustermann’
};
@Event() nbButtonEvent: EventEmitter<string>;
render() {
return <button
class={[
"font-medium leading-7 px-4 py-1 rounded-full text-sm uppercase",
...(this.secondary ? ["nb-secondary"] : []),
...(this.disabled ? ["nb-disabled"] : [])
].join(’ ’)}
onClick={() => this.nbButtonEvent.emit(this.getText())}
part="nb-button"
type="button"
>
<slot>{this.getText()}</slot>
</button>;
}
Erzeugung mit Stencil
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274514/nearbuy-plugin-marketplace.png)
Plug-in-Marktplatz
Plug-in-Installation
Visualisierung im Frontend
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9274513/nearbuy-plugin.png)
Plug-in-Oberfläche
Plug-in-Installation
Visualisierung im Frontend
Vertiefungen
1/3 Globales Theming trotz Shadow DOM
button {
background-color: var(--primary-color, #92BE9B);
font-family: ’Quicksand’, sans-serif;
@apply font-bold shadow-md text-white;
}
body.theme-muesli-index nb-button[secondary]::part(nb-button):hover {
background-color: rgba(72 111 80 / 4%);
}
CSS Custom Property
CSS Shadow Parts
2/3 API-Versionierung
Semantic
2/3 API-Versionierung
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275096/nodejs.png)
Semantic
Periodic
2/3 API-Versionierung
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275092/kubernetes-logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275096/nodejs.png)
Semantic
Periodic
Deprecation
https://api.nearbuy-food.de/v1/public/companies
3/3 Weiterentwicklung des Kubernetes-Clusters
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275108/istio.png)
Istio
3/3 Weiterentwicklung des Kubernetes-Clusters
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275107/flagger.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275108/istio.png)
Istio
Flagger
3/3 Weiterentwicklung des Kubernetes-Clusters
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275109/opa.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275107/flagger.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/1142422/images/9275108/istio.png)
Istio
Flagger
OPA
Zusammenfassung, Fazit und Ausblick
- Entwurf
- Plug-in-System
- Plug-in + Dienst
- Plug-in-Marktplatz
- Gegenvorschlag
- Keycloak
- Backend-Auth
- Design über Webkomponenten
- React, Vue, Nuxt
- React, Vue, Nuxt
- Empfehlungen für Kubernetes
- Codeanalyse, -stil & CI
✅
✅
✅
✅
✅
nearbuy
Quarkus
Frontend
React
Backend
nearbuy
Quarkus
Frontend
React
Backend
Kubernetes
Orchestrierung
Plug-in-Installation
Persistenz im Backend
CREATE TABLE plugin_installation (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
plugin_id TEXT NOT NULL,
owner_person UUID REFERENCES person ON DELETE CASCADE,
owner_company UUID REFERENCES company ON DELETE CASCADE,
date_installed TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
UNIQUE (plugin_id, owner_person),
UNIQUE (plugin_id, owner_company),
CONSTRAINT either_person_or_company CHECK (num_nonnulls(owner_person, owner_company) = 1)
)
PostgreSQL
Entwurf einer dienstorientierten Plug-in-Architektur für Webanwendungen
By Jonas Thelemann
Entwurf einer dienstorientierten Plug-in-Architektur für Webanwendungen
- 63