Aggregate account observations and capture
Query named existing views across routed accounts without confusing their units or creating a global history.
Use named aggregates when your application needs one read surface over several existing account views. They sample configured observation/capture stores; they do not submit messages or create durable history.
Aggregate minimal observations
import { Effect, Layer } from "effect";
import { EmailRouter, emailLayer, observedEmailRouterLayer } from "@corven/email-sdk";
import {
EmailObservationAggregate,
observedEmailSenderLayer,
} from "@corven/email-sdk/observations";
import { recordingSenderLayer } from "@corven/email-sdk/testing";
const first = emailLayer().pipe(
Layer.provideMerge(observedEmailSenderLayer(Layer.fresh(recordingSenderLayer), { capacity: 2 })),
);
const second = emailLayer().pipe(
Layer.provideMerge(observedEmailSenderLayer(Layer.fresh(recordingSenderLayer), { capacity: 3 })),
);
const router = observedEmailRouterLayer(
[
{ layer: first, name: "first" },
{ layer: second, name: "second" },
],
{ primary: "first" },
);
export const fictionalObservationAggregation = Effect.gen(function* queryNamedObservations() {
const route = yield* (yield* EmailRouter).select({ primary: "second" });
yield* route.send({
body: { text: "A fictional fact aggregation example." },
from: "sender@fictional.invalid",
subject: "Fictional notification",
to: "recipient@fictional.invalid",
});
return yield* (yield* EmailObservationAggregate).snapshot;
}).pipe(Effect.provide(router));
observedEmailRouterLayer keeps named account views available through EmailObservationAggregate. Each source identifies its unit: SenderInvocation or NativeOperation. Retained counts and truncation describe the samples, not lifetime sends or deliveries.
Aggregate private capture
import { Effect, Layer } from "effect";
import { Email, emailLayer, capturedEmailRouterLayer } from "@corven/email-sdk";
import { EmailCaptureAggregate, capturedEmailSenderLayer } from "@corven/email-sdk/capture";
import { recordingSenderLayer } from "@corven/email-sdk/testing";
const options = {
capacity: 100,
maxBytes: 1_048_576,
privateData: "AuthorizePayloadCapture",
} as const;
const primary = emailLayer().pipe(
Layer.provideMerge(capturedEmailSenderLayer(Layer.fresh(recordingSenderLayer), options)),
);
const backup = emailLayer().pipe(
Layer.provideMerge(capturedEmailSenderLayer(Layer.fresh(recordingSenderLayer), options)),
);
const router = capturedEmailRouterLayer(
[
{ layer: primary, name: "primary" },
{ layer: backup, name: "backup" },
],
{ fallbacks: ["backup"], primary: "primary" },
);
export const fictionalAggregateCapture = Effect.gen(function* inspectFictionalAccounts() {
yield* (yield* Email).send({
body: { text: "A fictional recording example." },
from: "sender@fictional.invalid",
subject: "Fictional aggregate capture",
to: "recipient@fictional.invalid",
});
return yield* (yield* EmailCaptureAggregate).snapshot;
}).pipe(Effect.provide(router));
capturedEmailRouterLayer supplies EmailCaptureAggregate over authorized capture views. Read retainedRecords, retainedBytes, source snapshots and truncated; keep payloads private.
Keep acquisition and failure semantics
Reuse the same Layer values used by sending so queries observe the same acquired stores. Source names must be distinct bounded identifiers, not private account values. Aggregation returns its exported aggregate error if a sample cannot be read or validated.
Samples across sources are not globally atomic. Missing data, eviction or a closed view does not prove non-submission and must not reset route/key recovery evidence.