add complete parent-bff implementation including: - GraphQL endpoint with depth/cost validation - ChildGuard越权校验 with redis cache and singleflight - parallel orchestration with partial failure fallback - three-level cache fallback strategy (Redis + LRU + downstream) - Kafka consumer for cache invalidation and notification push - opossum circuit breaker for downstream services - Prometheus metrics and SLO alerts - Helm chart for k8s deployment with multi-environment configs - Grafana dashboard for observability - complete unit and integration tests
78 lines
2.2 KiB
TypeScript
78 lines
2.2 KiB
TypeScript
import { logger } from "../shared/observability/logger.js";
|
||
|
||
/**
|
||
* Orchestrator:并行下游编排 + 降级标记(对齐 02-architecture-design.md §1.2 + §9 #6)。
|
||
*
|
||
* 核心能力:
|
||
* - Promise.allSettled 并行执行多个下游调用
|
||
* - 任一失败时记录 warn 日志 + 标记 partial=true
|
||
* - 失败的字段返回 null,其他字段正常返回
|
||
*
|
||
* P4 用 Promise.allSettled 降级;P6 引入 opossum 熔断器后,
|
||
* Orchestrator 将先检查熔断器状态再决定是否调用。
|
||
*/
|
||
|
||
export interface OrchestrationFailure {
|
||
key: string;
|
||
error: unknown;
|
||
}
|
||
|
||
export interface OrchestrationResult {
|
||
/** 各任务结果(失败的 key 值为 null) */
|
||
results: Record<string, unknown>;
|
||
/** 是否有部分失败 */
|
||
partial: boolean;
|
||
/** 失败详情 */
|
||
failures: OrchestrationFailure[];
|
||
}
|
||
|
||
/**
|
||
* 并行执行多个下游调用,部分失败容忍。
|
||
*
|
||
* @param tasks - key → Promise 的映射
|
||
* @returns OrchestrationResult
|
||
*
|
||
* @example
|
||
* ```ts
|
||
* const result = await orchestrate({
|
||
* parent: iamClient.getUserInfo(id),
|
||
* children: iamClient.getChildrenByParent(id),
|
||
* });
|
||
* const parent = result.results.parent as UserInfoDto | null;
|
||
* const children = result.results.children as ChildDto[] | null;
|
||
* if (result.partial) { // 有部分失败 }
|
||
* ```
|
||
*/
|
||
export async function orchestrate(
|
||
tasks: Record<string, Promise<unknown>>,
|
||
): Promise<OrchestrationResult> {
|
||
const entries = Object.entries(tasks);
|
||
const settled = await Promise.allSettled(
|
||
entries.map(([, promise]) => promise),
|
||
);
|
||
|
||
const results: Record<string, unknown> = {};
|
||
const failures: OrchestrationFailure[] = [];
|
||
|
||
entries.forEach(([key], index) => {
|
||
const settledResult = settled[index];
|
||
if (settledResult && settledResult.status === "fulfilled") {
|
||
results[key] = settledResult.value;
|
||
} else if (settledResult && settledResult.status === "rejected") {
|
||
results[key] = null;
|
||
const error = settledResult.reason;
|
||
failures.push({ key, error });
|
||
logger.warn(
|
||
{ key, err: error },
|
||
"Orchestrator: downstream call failed",
|
||
);
|
||
}
|
||
});
|
||
|
||
return {
|
||
results,
|
||
partial: failures.length > 0,
|
||
failures,
|
||
};
|
||
}
|