Ambiten Core v1.3.0 verbessert die Verknüpfung zwischen der Anwendungskonfiguration und dem Laufzeitverhalten. Schemas und Modelloptionen können in externen Modulen gespeichert werden, JSON-Schemas können Werte deklarieren, die während der Ausführung aufgelöst werden, und die Bootstrap-Konfiguration kann direkt mit einem externen Tenant-Konfigurations-Resolver verbunden werden.
Mit dieser Version wird zudem die Erstellung konfigurierter Protokolldateien beim Systemstart wiederhergestellt, die generierten Anwendungen werden aktualisiert, um die aktuelle Core-Version anzufordern, und es werden Kompatibilitätsprobleme bei den Paketen zwischen ESM, CommonJS, Browser-Builds und Windows-Build-Tools behoben.
In Version 1.2.4 wurde verdeutlicht, wie Kontext, Modelle, Anbieter und Mandanteninfrastruktur zusammenwirken. Version 1.3.0 baut auf diesen Rahmenbedingungen auf, indem sie deren Konfiguration in einer Anwendung vereinfacht.
Die Konfiguration legt fest, was eine Anwendung benötigt. Module organisieren deren Definitionen. Der Ausführungskontext stellt die Werte bereit, die zum aktuellen Vorgang gehören.
Eine Konfiguration, die mit der Anwendung mitwachsen kann
Die Inline-Konfiguration ist für eine kleine Anwendung nach wie vor sinnvoll. Ein Schema mit wenigen Feldern und ein Modell mit einem Sammlungsnamen lassen sich in einer einzigen JSON-Datei leicht nachvollziehen.
Mit zunehmendem Umfang der Anwendung können diese Definitionen den größten Teil der Konfigurationsdatei einnehmen. Um eine Verbindungseinstellung oder eine Mandantenoption zu überprüfen, muss man sich daher durch ein immer umfangreicheres Dokument arbeiten.
In Version 1.3.0 wurde die schema und model
Eigenschaften unterstützen Verweise auf externe Module. Jeder Verweis
bezeichnet eine Datei und den Export, den „Bootstrap“ laden soll.
Diese Eigenschaften können beispielsweise in folgenden Fällen auftreten:
ambiten.config.json:
{
"schema": {
"path": "./src/schemas/user.schema.mjs",
"exportName": "userSchema"
},
"model": {
"path": "./src/models/user.model.mjs",
"exportName": "userModelOptions"
}
}
Das Schema-Modul exportiert ein Schema-Definitionsobjekt:
// src/schemas/user.schema.mjs
export const userSchema = {
tenantId: {
type: "string",
required: true,
default: {
$runtime: "context",
key: "tenantId"
}
},
publicId: {
type: "string",
default: {
$runtime: "uuid"
}
},
username: {
type: "string",
required: true
},
email: {
type: "string",
required: true
},
createdAt: {
type: "date",
required: true,
default: {
$runtime: "now"
}
},
isDeleted: {
type: "boolean",
default: false
},
deletedAt: {
type: "date"
}
};
Das Modellmodul exportiert Modelloptionen:
// src/models/user.model.mjs
export const userModelOptions = {
collectionName: "users"
};
Bootstrap lädt diese Objekte und baut die Schema- und Modellinfrastruktur auf. Inline-Definitionen werden weiterhin unterstützt, sodass jede Anwendung die Organisationsstruktur wählen kann, die ihrer Größe entspricht.
Pfade in einer JSON-Konfigurationsdatei werden relativ zu dieser
Datei aufgelöst. Pfade in einem programmgesteuert übergebenen Konfigurationsobjekt
werden relativ zum Arbeitsverzeichnis des Prozesses aufgelöst.
Das Weglassen von exportName wählt den Standardexport aus.
Die bestehende features.schemas und
features.models Die Werte beschreiben die Standorte
der Scaffolding-Elemente. Das Laden von Modulen zur Laufzeit wird über die
schema und model Eigenschaften; diese
Feature-Verzeichnisse werden nicht automatisch durchsucht.
Laufzeit-Standardwerte, die pro Dokument festgelegt werden
Manche Dokumentwerte sind nur dann aussagekräftig, wenn eine Operation ausgeführt wird. Ein Erstellungszeitstempel gehört zu dem Dokument, das gerade erstellt wird. Für dieses Dokument sollte eine generierte Kennung erstellt werden. Eine Mandanten-Kennung hängt von der aktiven Ausführung ab.
Mit Version 1.3.0 werden deklarative Funktionen eingeführt $runtime Standardwerte,
die diese Anforderungen in einem JSON-kompatiblen Schema ausdrücken.
-
{ "$runtime": "now" }erstellt einDatewenn die Standardeinstellung angewendet wird. -
{ "$runtime": "uuid" }erzeugt eine UUID. -
{ "$runtime": "context", "key": "tenantId" }liest die Mandanten-ID aus dem aktivenAmbitenContext.
Unterstützung für Kontext-Standardwerte tenantId,
requestId, dbName, und
collectionName.
Anhand des obigen Schemas kann die Validierung die fehlenden Werte innerhalb eines Ausführungskontexts ermitteln:
import {
AmbitenContext,
Schema
} from "@ambiten/core";
import {
userSchema
} from "./schemas/user.schema.mjs";
const schema = Schema.create(userSchema);
const document = {
username: "Alice",
email: "alice@example.com"
};
await AmbitenContext.run(
{ tenantId: "tenant-a" },
async () => {
await schema.validateAsync(document);
}
);
In diesem Beispiel wird eine Schemavalidierung ohne Schreibvorgang in die Datenbank durchgeführt.
Bei der Validierung werden die Mandanten-ID, eine UUID, ein
Erstellungszeitstempel sowie die isDeleted Standardmäßig im Dokument.
Standardwerte gelten, wenn ein Feld undefined.
Die angegebenen Werte unterliegen weiterhin einer Validierung. Die Standardwerte von Objekten und Arrays
werden für jedes Dokument kopiert, wodurch verhindert wird, dass verschiedene
Dokumente denselben veränderbaren Standardwert gemeinsam nutzen.
Nicht unterstützte Laufzeitanweisungen und Kontextschlüssel werden abgelehnt. Ein Kontextstandardwert schlägt ebenfalls fehl, wenn der angeforderte Wert während der Ausführung nicht verfügbar ist.
Für Inline-Schemas und externe
Schema-Module gilt derselbe Standardvertrag: Verwenden Sie JSON-kompatible Literalwerte oder unterstützte
$runtime Deskriptoren.
Für das Anlegen und das Löschen gelten unterschiedliche Standardeinstellungen
In diesem Beispiel wird bewusst deletedAt
optional und ohne Standardwert.
Ein neu erstelltes Dokument enthält isDeleted: false
und ohne Löschzeitstempel. Ein Lösch-Workflow kann ein
Datum festlegen, an dem die Löschung erfolgt.
A date In diesem Feld muss ein gültiger Wert eingegeben werden
Date Wert, sofern angegeben. Zuweisung
false bis deletedAt verstößt gegen diesen
Typ, während die fälschlicherweise als Standard festgelegte aktuelle Uhrzeit
bei der Erstellung einen Zeitstempel für die Löschung aufzeichnet.
Konfiguration externer Mandanten über Bootstrap
Ambiten trennt bereits die Identität des Mandanten von der
Mandanteninfrastruktur. Ein Request-Resolver identifiziert den Mandanten,
AmbitenContext trägt diese Identität in sich, und
MultiTenantManager löst die entsprechenden
Datenbankressourcen auf.
In Version 1.3.0 wird der JSON-Konfigurationspfad um Folgendes erweitert:
multiTenant.tenantConfigResolverModule.
Anwendungen können nun einen externen Tenant-Konfigurations-Resolver
über dasselbe „File-and-Export“-Muster anbinden.
{
"multiTenant": {
"enabled": true,
"tenants": {},
"tenantConfigResolverModule": {
"path": "./src/tenantResolver.mjs",
"exportName": "resolveTenantConfig"
},
"initOptions": {
"lazy": true
}
}
}
Der ausgewählte Export ist eine Funktion, die eine Mandanten-ID als Parameter entgegennimmt.
Sie gibt die Mandantenkonfiguration zurück oder undefined oder
null wenn das Problem mit dem Mieter nicht gelöst werden kann.
Eine kleine Anwendung könnte eine bestimmte Menge von Mandanten- Identifikatoren aus den von der Umgebung bereitgestellten Verbindungszeichenfolgen auflösen:
// src/tenantResolver.mjs
const tenantUris = new Map([
["tenant-a", process.env.TENANT_A_MONGODB_URI],
["tenant-b", process.env.TENANT_B_MONGODB_URI]
]);
export function resolveTenantConfig(tenantId) {
const uri = tenantUris.get(tenantId);
if (!uri) {
return undefined;
}
return { uri };
}
Die Funktion kann auch asynchron ablaufen, wenn die Konfiguration aus einer Steuerungsdatenbank, einem Kontodienst, einer Konfigurations-API oder einer anderen anwendungsspezifischen Quelle stammt.
Die aufgelöste Konfiguration enthält einen Eintrag „MongoDB“ uri,
mit optionaler dbName und
metadata. Der MultiTenantManager verwaltet die Registrierung
und den Client-Status.
Statische Mandanten und dynamisch erkannte Mandanten können nebeneinander bestehen. Zunächst wird die lokale Registrierungsdatenbank überprüft; der externe Resolver liefert die Konfiguration für Mandanten, die nicht registriert sind.
Die Anforderung der Authentifizierung und Autorisierung obliegt weiterhin der Anwendung. Die Auflösung der Datenbankkonfiguration eines Mandanten gibt keinen Aufschluss darüber, ob ein Aufrufer auf diesen Mandanten zugreifen darf.
Beim Systemstart erstellte Protokolldateien
Die Bootstrap-Protokollierung sollte das konfigurierte Ziel bereits bei der Initialisierung der Anwendung bereitstellen.
In Version 1.3.0 wurde die Vorbereitung des übergeordneten Verzeichnisses und der Protokolldatei für deklarativ konfigurierte Dateitransporte wiederhergestellt. Der Bootstrap-Prozess ermittelt das Ziel, legt fehlende Verzeichnisse an und öffnet die Datei im Anfüge-Modus, bevor der Transport aufgebaut wird.
Eine explizite Konfiguration für die Konsole und die rotierende Datei könnte wie folgt aussehen:
{
"logger": {
"enabled": true,
"level": "info",
"transportConfigs": [
{
"type": "console",
"options": {}
},
{
"type": "rotating-file",
"options": {
"filename": "./logs/ambiten.log"
}
}
]
}
}
Relative Protokollziele werden ausgehend vom Arbeitsverzeichnis des Prozesses aufgelöst. Der Inhalt vorhandener Dateien bleibt erhalten, und ein Fehler bei der Vorbereitung des Ziels wird während der Initialisierung gemeldet.
Wenn die Protokollierung aktiviert ist und keine Transporte angegeben werden, verwendet Bootstrap seine Standardtransporte „console“ und „rotating-file“. Eine explizite Laufzeitkonfiguration, die ausschließlich die Konsole vorsieht, bleibt auf die Konsole beschränkt.
Anwendungen, die eine eigene Logger-Instanz oder ausführbare Transportkomponente bereitstellen, sind weiterhin für die Konfiguration dieser Transportkomponenten verantwortlich.
Das Laden der Module erfolgt während der Laufzeit der Anwendung
Externe Konfigurationsmodule nutzen das native Modul-Laden und unterstützen die Auswahl von Exporten aus ESM- und CommonJS-Modulen. Dateipfade werden in Datei-URLs umgewandelt, sodass auch Windows-Pfade in denselben Ladevorgang einbezogen werden können.
In den Beispielen dieses Artikels wird Folgendes verwendet: .mjs Dateien, sodass
ihr Modulformat eindeutig ist.
Anwendungen, die .ts Konfigurationsmodule
benötigen TypeScript-Unterstützung bei der Durchführung des nativen
Imports. Mit tsx Nach der Installation könnte ein Entwicklungsbefehl
wie folgt lauten:
node --import tsx src/main.ts
Ein CommonJS-„require“-Hook allein bietet diese native Importunterstützung nicht.
Bei kompilierten Bereitstellungen richten Sie die Konfigurationsverweise auf die generierten JavaScript-Dateien aus und fügen Sie diese Dateien in die Bereitstellung ein. Durch das Kompilieren der Anwendung werden die Modulpfade in der JSON-Konfiguration nicht automatisch neu geschrieben.
Stärkere Paketgrenzen
Das Laufzeitverhalten hängt auch davon ab, wie Pakete erstellt, installiert und importiert werden.
Diese Version enthält Korrekturen hinsichtlich der ausgegebenen ESM-Referenzen, der Kompatibilität über Paketgrenzen hinweg, der Kompilierung von Browser-Bundles sowie des Aufrufs des Windows-Paketmanagers.
- Die ESM-Modulverweise enthalten die Dateiendungen, die von Node.js benötigt werden.
- Die Überprüfungen zur Paketintegration umfassen die Nutzung von CommonJS und ESM in Core, den Adaptertypen, der Adapter-Laufzeitumgebung sowie dem Express-Adapter.
- Diese Prüfungen erstrecken sich auch auf den Anforderungskontext, der über die Grenzen der getesteten Core- und Adapter-Pakete hinweg verwendet wird.
- Fehler beim Erstellen des Browsers im Zusammenhang mit dem Import des Moduls „Node.js“ wurden behoben.
- Integrationsskripte für den Build- und Paketierungsprozess sorgen für eine zuverlässigere Ausführung von pnpm unter Windows.
Diese Änderungen unterstützen die in der vorherigen Version beschriebene Laufzeitarchitektur: Ein Adapter richtet den Ausführungskontext ein, und Anwendungsoperationen müssen in der Lage sein, denselben Kontext über die installierten Pakete zu beobachten.
Die zugehörigen Pakete behalten ihre eigenen Versionsnummern bei.
v1.3.0 bezeichnet diese Core-Version.
Bootstrapping mit der aktuellen Core-Version
Generierte Anwendungen enthalten nun folgende Deklaration:
{
"dependencies": {
"@ambiten/core": "latest"
}
}
Bei der erstmaligen Installation einer Abhängigkeit ermittelt der Paketmanager
die unter der Registry veröffentlichte Version
latest Tag.
Dadurch wird die Absicht des Scaffolds deutlich gemacht, und eine neu generierte Anwendung kann mit der aktuellen, mit einem Tag versehenen Core-Version starten – auch dann, wenn dieses Tag über den ursprünglichen Bereich der Hauptversionen hinausgeht.
Der installierte Abhängigkeitsbaum und die Lockdatei erfassen dann die ausgewählte Version. Bestehende Anwendungen steuern weiterhin die Aktualisierung von Abhängigkeiten über ihren üblichen Workflow der Paketverwaltung.
Die interaktiven Zusammenfassungen der Gerüste wurden ebenfalls aktualisiert, um der überarbeiteten Struktur der Modellkonfiguration Rechnung zu tragen.
Installation von Ambiten Core v1.3.0
Installieren oder aktualisieren Sie Core mit:
pnpm add @ambiten/core@1.3.0
Oder mit npm:
npm install @ambiten/core@1.3.0
So führen Sie die in dieser Version enthaltene Bootstrap-Befehlszeilenschnittstelle aus:
pnpm --package=@ambiten/core@1.3.0 dlx ambiten init my-app
Dieser Befehl wählt die CLI-Version 1.3.0 aus. Die generierte Anwendung
verwendet weiterhin die Kernabhängigkeit latest, das
aufgelöst wird, sobald seine Abhängigkeiten installiert sind.
Überprüfung der generierten Abhängigkeit „MongoDB“
In der Version 1.3.0 des Scaffolds ist eine Diskrepanz bei den Abhängigkeiten zu berücksichtigen:
Der generierte Bereich „MongoDB“ lautet
^6.14.2, während das veröffentlichte Core-Paket Folgendes angibt:
^5 || ^7 als dessen MongoDB-Peer-Bereich.
Stimmen Sie die Treiberversion der Anwendung auf die von Core angegebene
Kompatibilität und die Node.js-Anforderungen des ausgewählten Treibers ab.
Überprüfung der Konfiguration während eines Upgrades
- Bestehende Inline-Schema- und Modelldefinitionen können inline bleiben.
- Verweise auf externe Module müssen auf Dateien verweisen, die der laufenden Anwendung zur Verfügung stehen.
-
Schema-Standardwerte sollten unterstützte Literalwerte verwenden oder
$runtimeDeskriptoren. - Ein Kontextstandardwert setzt voraus, dass der angeforderte Wert verfügbar ist, wenn die Validierung den Standardwert anwendet.
- Für die Dateiprotokollierung ist ein Dateitransport in einer explizit angegebenen Transportliste erforderlich.
- Die Abhängigkeiten der Adapter sollten zusammen mit dem Core überprüft werden, wobei die jeweilige Release-Version des jeweiligen Pakets zugrunde gelegt werden sollte.
Dokumentation im Anschluss an die Laufzeit
Die Dokumentation zu Version 1.3.0 umfasst Schema- Definitionen, Laufzeiteinstellungen, Mandantenfähigkeit, externe Konfigurationsmodule, die AmbitenBootstrapFactory, CLI- Scaffolding sowie fortgeschrittene Anwendungsmöglichkeiten.
In den Beispielen wird zwischen der beim Start geladenen Konfiguration und dem für eine einzelne Anfrage, einen Vorgang oder einen Hintergrundjob festgelegten Zustand unterschieden.
Außerdem wird erläutert, wie Anwendungen Transaktionsgrenzen festlegen, die Mandantenidentität in die Ausführung durch die Worker übertragen, die Protokollierung konfigurieren und das Herunterfahren von Ressourcen koordinieren.
Diese Unterscheidungen helfen Entwicklern dabei, ein funktionierendes Beispiel mit den Aufgaben einer größeren Anwendung in Verbindung zu bringen.
Von der Konfiguration bis zur Ausführung
Eine Anwendung kann mit einem kleinen Inline-Schema beginnen, ihre Definitionen bei zunehmendem Umfang in Module auslagern und eine externe Mandantenkonfigurationsquelle über Bootstrap einbinden. Laufzeitstandardwerte liefern dann Dokumentwerte aus der Ausführung, in der diese Dokumente validiert werden.
Ambiten Core v1.3.0 erleichtert die Darstellung dieser Entscheidungen, wobei die Trennung zwischen wiederverwendbarer Infrastruktur und ausführungsspezifischem Zustand gewahrt bleibt.
Entdecken Sie die Ambiten-Dokumentation, sehen Sie sich die Quelle auf GitHub, oder suchen Sie @ambiten/core on npm.