Προσαρμοσμένα behaviors
Ένα behavior υλοποιεί το IPipelineBehavior: μία μοναδική μέθοδο, handle(context, next). Εκτελεί
κώδικα πριν από το next(), μετά από αυτό, ή επιστρέφει αποτέλεσμα χωρίς να το καλέσει.
import type { IPipelineBehavior, IPipelineContext, NextDelegate,} from '@cqrs-ddd/pipeline';
export class TimingBehavior implements IPipelineBehavior { constructor(private readonly record: (name: string, ms: number) => void) {}
async handle(context: IPipelineContext, next: NextDelegate): Promise<unknown> { const start = performance.now(); try { return await next(); } finally { this.record(context.requestName, performance.now() - start); } }}Ένα behavior είναι μια απλή κλάση. Δώστε στο pipeline ένα instance όταν ο constructor του χρειάζεται ορίσματα. Ένα behavior που δηλώνεται χωρίς instance δημιουργείται χωρίς ορίσματα constructor.
const pipeline = createPipeline({ behaviors: [new TimingBehavior((name, ms) => histogram.record(ms, { name }))], globalBehaviors: { before: [TimingBehavior] },});Ελέγξτε τα υποχρεωτικά ορίσματα του constructor μέσα σε αυτόν και ρίξτε ένα TypeError, ώστε ένα
missing dependency να αποτύχει κατά τη δημιουργία του pipeline και όχι κατά την πρώτη κλήση.
Επιλογές ανά λειτουργία (Options per operation)
Ενότητα με τίτλο «Επιλογές ανά λειτουργία (Options per operation)»Ένα call site περνάει επιλογές ως tuple [Behavior, options]. Το behavior διαβάζει τις συγχωνευμένες
επιλογές με το context.getBehaviorOptions(Behavior). Κάντε export έναν helper που κατασκευάζει το tuple,
όπως κάνει κάθε πακέτο:
import type { PipelineBehaviorTuple } from '@cqrs-ddd/pipeline';
export interface TimingOptions { /** Κλήσεις πιο αργές από αυτά τα χιλιοστά του δευτερολέπτου καταγράφονται. @default 0 */ threshold?: number;}
export function timing( options: TimingOptions = {},): PipelineBehaviorTuple<TimingBehavior, TimingOptions> { return [TimingBehavior, options];}
// μέσα στο handle():const { threshold = 0 } = context.getBehaviorOptions<TimingOptions>(TimingBehavior) ?? {};Επιλογές που δίνονται τόσο καθολικά όσο και σε ένα call site συγχωνεύονται επιφανειακά (shallow merge), με τα κλειδιά του call site να υπερισχύουν. Δείτε Σειρά εκτέλεσης.
Κοινή χρήση τιμών με άλλα behaviors
Ενότητα με τίτλο «Κοινή χρήση τιμών με άλλα behaviors»Το context.items μεταφέρει τιμές από το ένα behavior στο επόμενο. Κάντε export ένα typed token για
κάθε τιμή που μπορεί να διαβάσει ένα άλλο behavior:
import { createPipelineItem, setPipelineItem } from '@cqrs-ddd/pipeline';
export const DURATION_ITEM = createPipelineItem<number>('timing.duration');
setPipelineItem(context, DURATION_ITEM, elapsed);Αντικατάσταση του input
Ενότητα με τίτλο «Αντικατάσταση του input»Ένα behavior που παράγει ένα νέο input, όπως ένα parsed αντίγραφο, το παραδίδει στη συνάρτηση με το
replaceRequest(context, value) αντί να τροποποιήσει το αντικείμενο του καλούντος. Τα επόμενα behaviors
βλέπουν τότε την αντικατάσταση ως context.request.
Ένα contract
Ενότητα με τίτλο «Ένα contract»Μια κλάση behavior μπορεί να δηλώσει ένα contract κάτω από το well-known symbol
PIPELINE_BEHAVIOR_CONTRACT: κανόνες σειράς έναντι άλλων behaviors, και μια συνάρτηση validate
που ελέγχει τις ενεργές επιλογές κάθε λειτουργίας. Το pipeline εκτελεί το contract μία φορά ανά
λειτουργία, πριν από την πρώτη εκτέλεσή της. Δείτε
Πώς γίνεται wrap μια λειτουργία.
import { type IPipelineBehaviorContract, PIPELINE_BEHAVIOR_CONTRACT,} from '@cqrs-ddd/pipeline';
export class TimingBehavior implements IPipelineBehavior { static readonly [PIPELINE_BEHAVIOR_CONTRACT]: IPipelineBehaviorContract = { order: { before: ['CacheBehavior'] }, validate: (context) => { const threshold = (context.effectiveOptions as TimingOptions | undefined)?.threshold; if (threshold === undefined || threshold >= 0) return undefined; return [ { handlerName: context.handlerName, behaviorName: 'TimingBehavior', message: `threshold must not be negative, received ${threshold}`, fix: 'Pass a threshold of 0 or more.', }, ]; }, }; // ...}Ένας κανόνας σειράς ονομάζει άλλα behaviors με βάση την κλάση ή το string ταυτότητάς τους και εφαρμόζεται
μόνο όταν αυτά είναι παρόντα. Για να απαιτήσετε ένα peer behavior, ελέγξτε το context.effectiveBehaviorTypes
στο validate.
Σταθερή ταυτότητα (Stable identity)
Ενότητα με τίτλο «Σταθερή ταυτότητα (Stable identity)»Ένα behavior ταυτοποιείται από την κλάση του. Όταν ενδέχεται να φορτωθούν δύο αντίγραφα ενός πακέτου,
δώστε στην κλάση ένα σταθερό string στο PIPELINE_BEHAVIOR_ID, ώστε και τα δύο αντίγραφα να αναγνωρίζονται
ως ένα ενιαίο behavior:
import { PIPELINE_BEHAVIOR_ID } from '@cqrs-ddd/pipeline';
export class TimingBehavior implements IPipelineBehavior { static readonly [PIPELINE_BEHAVIOR_ID] = 'my-package:TimingBehavior';}Logging
Ενότητα με τίτλο «Logging»Γράψτε μέσω ενός PipelineLogger που λαμβάνεται ως προαιρετικό όρισμα constructor, με προεπιλογή
το console. Ένα NestJS LoggerService και τα περισσότερα structured loggers ικανοποιούν το interface.