Skip to content

Architecture

1. System Context (C4 L1)

Pricing is purely synchronous on the read/calculate path (sale calls it inline at checkout). The only async input is the ProductVariant CDC stream that auto-seeds fares.

2. Container View (C4 L2)

3. Component View (C4 L3) — Internal Layering

LayerResponsibility
Routes12 base paths in RestPaths (one — pricing-configurations — declared, no controller mounted)
ControllersAuth gate (jwt, basic), resource-based permission, DTO mapping
Calculation servicesStateless fare/tax math; v1 flat, v2 snapshot
Management servicesCRUD + aggregate transactions
RepositoriesDrizzle queries, soft-delete, counter denormalization
WorkerCDC ingestion, idempotent fare seeding

4. State Machines Index

EntityStatesDiagram
FareSetACTIVATED, DEACTIVATED
FareACTIVATED, DEACTIVATED, ARCHIVED
Tax / TaxSet / TaxTypeACTIVATED, DEACTIVATED, ARCHIVED
PromotionDRAFT, ACTIVATED, DEACTIVATED, ARCHIVED

FareSet

FareSetStatuses.canDeactivate() always returns false — an activated FareSet is terminal-active. Exactly one ACTIVATED FareSet per variant is the selection invariant.

Fare

Tax / TaxSet / TaxType

All three share IGNIS Statuses.{ACTIVATED, DEACTIVATED, ARCHIVED}. The tax calculator only reads ACTIVATED rows within their effective window.

Promotion

Promotions are CRUD-only today — no calculator consumes them, so state has no pricing effect yet.

5. Runtime Scenarios

5.1 Checkout Pricing (v2 snapshot)

StepDetail
2–7Per-line: fare selected (DEFAULT/OVERRIDE/POLICY), then compound priority-grouped taxes
8Order snapshot aggregates per-line byBearer ledgers + order-level rules
9–10Sale persists the immutable snapshot; pricing keeps no order state

5.2 ProductVariant CDC → Fare Seeding

StepDetail
4Only CREATE/RETRIEVE handled; UPDATE/DELETE are no-ops
5Skips when a FareSet already exists for the variant (idempotent)
6FBT bundlers seed an OVERRIDE child carrying orderProductVariantIds CONTAINS leadVariantId

5.3 Synced Variant — FareSet Deep Copy

When a variant carries metadata.referenceId, the worker deep-copies the reference variant's full fare structure inside one transaction instead of seeding a fresh default.

6. Crosscutting Concerns

ConcernHow this service handles it
AuthNJWT (ES256, JWKS from identity); BASIC for service-to-service (sale uses BASIC)
AuthZCasbin via PolicyDefinition; resource-based per controller; permissions cached in Redis
i18njsonb { default, en, vi } on name / description columns
LoggingIGNIS structured (key: %s); @logged() decorators; SKIP/DONE markers in worker
Moneyfloat(x, 4) everywhere; amounts stored as decimal(15,4), serialized as strings (MoneySchema)
IdempotencyCDC worker skips when FareSet/FBT override already seeded; calculate endpoints are side-effect-free
StatelessnessPricing persists no order/snapshot; the snapshot it returns is owned by the caller
Soft-deleteSoftDeletableRepository (deletedAt); default filter deletedAt IS NULL
IDsSnowflake via IdGenerator, worker 7

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