Μετάβαση στο περιεχόμενο

Προσαρμοσμένα 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 και όχι κατά την πρώτη κλήση.

Ένα 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 να υπερισχύουν. Δείτε Σειρά εκτέλεσης.

Το 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);

Ένα behavior που παράγει ένα νέο input, όπως ένα parsed αντίγραφο, το παραδίδει στη συνάρτηση με το replaceRequest(context, value) αντί να τροποποιήσει το αντικείμενο του καλούντος. Τα επόμενα behaviors βλέπουν τότε την αντικατάσταση ως context.request.

Μια κλάση 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.

Ένα 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';
}

Γράψτε μέσω ενός PipelineLogger που λαμβάνεται ως προαιρετικό όρισμα constructor, με προεπιλογή το console. Ένα NestJS LoggerService και τα περισσότερα structured loggers ικανοποιούν το interface.