Skip to content

Architecture

@nx/search is a library; it has no standalone runtime. All diagrams show it embedded in the host (commerce) process.

1. System Context (C4 L1)

2. Container View (C4 L2)

3. Component View (C4 L3) — Internal Layering

LayerResponsibility
Mixin / SearchControllerHTTP surface, auth + permission gate, scope merge
SearchServiceIgnis filter → Typesense params, include resolution, response shaping
TypesenseConverterwhere/order/fieldsfilter_by/sort_by/include_fields
CDC servicesConsume, batch, route, cascade, enrich, index
MappersDB row (snake/camel) → Typesense doc; null for soft-deleted
CollectionRegistrySingleton registry of the 8 configs (+ embed.from wiring)
HelpersTypesense client, schema-drift comparison, include parsing

4. State Machines Index

The only stateful runtime element is the CDC circuit breaker (opt-in).

ElementStatesDiagram
CDC circuit breakerCLOSED, OPEN→ jump

CDC Circuit Breaker

FromEventToGuards
CLOSEDhealth probe failsOPENAPP_ENV_CDC_CIRCUIT_BREAKER_ENABLED=true
OPENprobe recoversCLOSEDquiet window elapsed (Typesense 30s / Google 90s)
OPENOPENcapped at maxOpenMs (30 min)

When OPEN, the consumer pauses (auto-commit is off, so offsets are not advanced) — messages are re-read after recovery.

5. Runtime Scenarios

5.1 Query path (consumer → Typesense)

StepDetail
2resolveSearchScope() injects tenant/merchant where; defaults to null (open) unless host overrides
5If disableSemanticSearch is set, the embedding field is dropped from query_by (strict keyword)
7include relations resolved via Typesense native join (reference) or hydration lookup

5.2 CDC sync (Debezium → Typesense)

StepDetail
3Batch tuned by CDCBatchingConfig (200 max, 2000ms flush) + maxBytes 5MB
5Mapper returns null for soft-deleted → triggers Typesense delete
6Cascade sources: ProductCategory, MetaLink, FareSet/Fare, ProductBundler, most inventory tables
8Auto-commit off — offsets advance only after the batch succeeds (at-least-once)

5.3 Schema-drift check on bootstrap

Divergence is logged, never auto-applied — auto-rebuild would drop documents. Use the backfill scripts for additive changes.

6. Crosscutting Concerns

ConcernHow this library handles it
AuthNInherited from host — SearchController + mixin accept JWT or Basic (AuthenticateStrategy.JWT, .BASIC)
AuthZPermissions search:search / search:search-count; mixin requires an authorize config from the consumer
Multi-tenancyresolveSearchScope() injects a where scope, and-merged with caller filter (host overrides per entity)
i18n{ en, vi } flattened to name.en / name.vi Typesense fields; future locales caught by wildcard fields
LoggingStructured key-value (key: %s) via IGNIS logger
IdempotencyLSN/version guards (source_lsn, deleted_at) reject out-of-order CDC events
Soft-deleteMapper returns null for deletedAt != null → document deleted from Typesense
ResilienceCircuit breaker (opt-in) + DLQ for poison messages + retry utility
IDsDocument id = source row id; library allocates no Snowflake IDs

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.