Time-bounded, scoped price rules with overlap prevention and deterministic resolution
| Tier | domain |
| Role | unclassified (baselined) |
| Path | crates/domain/pricing-rules |
| Edition | 2021 |
| Targets | domain_pricing_rules |
| Public items | 38 across 0 modules |
| Tests | 5 |
What it is for
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
A "price" is a rule row that applies to some subject (what you're pricing) under some scope (who/where/channel/etc), within a time window.
Key features:
- JSONB scope for flexibility (
party_id, channel,membership_tier, etc.) PostgreSQLexclusion constraint prevents overlapping validity windows- Deterministic resolution: specificity > priority >
valid_from> id
Capabilities
NewPrice
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub struct NewPrice |
Price
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub struct Price |
Price :: fn builder(subject_type : impl Into <String>, subject_id : Uuid) -> PriceBuilder |
Price :: fn to_money(& self) -> Money |
Price :: fn is_valid_at(& self, at : DateTime <Utc>) -> bool |
Price :: fn specificity(& self) -> usize |
async fn resolve_catalog_item_price(pool : & PgPool, item_id : Uuid, context : & PricingContext,) -> Result <ResolvedPrice> |
async fn resolve_catalog_item_prices(pool : & PgPool, item_ids : & Uuid, context : & PricingContext,) -> Result <Vec <(Uuid, Option <ResolvedPrice>)>> |
PriceBuilder
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub struct PriceBuilder |
PriceBuilder :: fn new(subject_type : impl Into <String>, subject_id : Uuid) -> Self |
PriceBuilder :: fn scope(mut self, scope : JsonValue) -> Self |
PriceBuilder :: fn amount_minor(mut self, amount : i64) -> Self |
PriceBuilder :: fn money(mut self, money : & Money) -> Self |
PriceBuilder :: fn currency(mut self, currency : impl Into <String>) -> Self |
PriceBuilder :: fn valid_from(mut self, from : DateTime <Utc>) -> Self |
PriceBuilder :: fn valid_to(mut self, to : DateTime <Utc>) -> Self |
PriceBuilder :: fn priority(mut self, priority : i32) -> Self |
PriceBuilder :: fn reason(mut self, reason : impl Into <String>) -> Self |
PriceBuilder :: fn created_by(mut self, user_id : Uuid) -> Self |
PriceBuilder :: fn build(self) -> Result <NewPrice> |
PriceRepository
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub struct PriceRepository |
PriceRepository :: async fn insert(pool : & PgPool, price : NewPrice) -> Result <Price> |
PriceRepository :: async fn find_by_id(pool : & PgPool, id : Uuid) -> Result <Price> |
PriceRepository :: async fn list_for_subject(pool : & PgPool, subject_type : & str, subject_id : Uuid,) -> Result <Vec <Price>> |
PriceRepository :: async fn resolve(pool : & PgPool, subject_type : & str, subject_id : Uuid, context : & PricingContext, at : Option <DateTime <Utc>>,) -> Result <ResolvedPrice> |
PriceRepository :: async fn delete(pool : & PgPool, id : Uuid) -> Result <()> |
PriceRepository :: async fn deactivate(pool : & PgPool, id : Uuid) -> Result <Price> |
PriceRepository :: async fn end_at(pool : & PgPool, id : Uuid, end_time : DateTime <Utc>) -> Result <Price> |
PricingContext
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub struct PricingContext |
PricingContext :: fn new() -> Self |
PricingContext :: fn with(mut self, key : impl Into <String>, value : impl Into <JsonValue>) -> Self |
PricingContext :: fn with_party(self, party_id : Uuid) -> Self |
PricingContext :: fn with_channel(self, channel : impl Into <String>) -> Self |
PricingContext :: fn to_json(& self) -> JsonValue |
PricingRulesError
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub enum PricingRulesError |
ResolvedPrice
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub struct ResolvedPrice |
ResolvedPrice :: fn to_money(& self) -> Money |
Result
Pricing Rules: Time-bounded, scoped price rules with overlap prevention
| Item |
|---|
pub type Result<T>: std::result::Result <T, PricingRulesError> |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
No public modules: the crate root is its whole surface.
Public surface
`crate root`
| Item | What it is |
|---|---|
pub enum PricingRulesError | — |
pub type Result<T>: std::result::Result <T, PricingRulesError> | — |
pub struct Price | A price rule that applies to a subject under a scope within a time window |
Price :: fn builder(subject_type : impl Into <String>, subject_id : Uuid) -> PriceBuilder | Create a new price builder |
Price :: fn to_money(& self) -> Money | Convert amount to Money type |
Price :: fn is_valid_at(& self, at : DateTime <Utc>) -> bool | Check if this price is currently valid |
Price :: fn specificity(& self) -> usize | Get the specificity (number of scope keys) |
pub struct PriceBuilder | Builder for creating new prices |
PriceBuilder :: fn new(subject_type : impl Into <String>, subject_id : Uuid) -> Self | — |
PriceBuilder :: fn scope(mut self, scope : JsonValue) -> Self | Set the scope (who/where/channel this price applies to) |
PriceBuilder :: fn amount_minor(mut self, amount : i64) -> Self | Set the price amount in minor units (cents) |
PriceBuilder :: fn money(mut self, money : & Money) -> Self | Set the price from a Money value |
PriceBuilder :: fn currency(mut self, currency : impl Into <String>) -> Self | Set the currency |
PriceBuilder :: fn valid_from(mut self, from : DateTime <Utc>) -> Self | Set when this price becomes valid |
PriceBuilder :: fn valid_to(mut self, to : DateTime <Utc>) -> Self | Set when this price expires (exclusive) |
PriceBuilder :: fn priority(mut self, priority : i32) -> Self | Set the priority for tie-breaking (higher wins) |
PriceBuilder :: fn reason(mut self, reason : impl Into <String>) -> Self | Set the reason for this price (audit trail) |
PriceBuilder :: fn created_by(mut self, user_id : Uuid) -> Self | Set who created this price |
PriceBuilder :: fn build(self) -> Result <NewPrice> | Validate and build the price (does not persist) |
pub struct NewPrice | A new price ready to be inserted |
pub struct PricingContext | Context for price resolution - contains all attributes that might affect pricing The context is matched against price scopes using containment: context @> scope - the context must contain all keys in the price's scope |
PricingContext :: fn new() -> Self | — |
PricingContext :: fn with(mut self, key : impl Into <String>, value : impl Into <JsonValue>) -> Self | Add an attribute to the context |
PricingContext :: fn with_party(self, party_id : Uuid) -> Self | Add a party_id to the context |
PricingContext :: fn with_channel(self, channel : impl Into <String>) -> Self | Add a channel to the context |
PricingContext :: fn to_json(& self) -> JsonValue | Convert to JSON value for database query |
pub struct ResolvedPrice | The result of price resolution - the best matching price for a subject |
ResolvedPrice :: fn to_money(& self) -> Money | Convert to Money type |
pub struct PriceRepository | Repository for price operations |
PriceRepository :: async fn insert(pool : & PgPool, price : NewPrice) -> Result <Price> | Insert a new price |
PriceRepository :: async fn find_by_id(pool : & PgPool, id : Uuid) -> Result <Price> | Find a price by ID |
PriceRepository :: async fn list_for_subject(pool : & PgPool, subject_type : & str, subject_id : Uuid,) -> Result <Vec <Price>> | List all prices for a subject |
PriceRepository :: async fn resolve(pool : & PgPool, subject_type : & str, subject_id : Uuid, context : & PricingContext, at : Option <DateTime <Utc>>,) -> Result <ResolvedPrice> | Resolve the best matching price for a subject at a given time with context Resolution order (first criterion that differs wins): 1 |
PriceRepository :: async fn delete(pool : & PgPool, id : Uuid) -> Result <()> | Soft delete a price |
PriceRepository :: async fn deactivate(pool : & PgPool, id : Uuid) -> Result <Price> | Deactivate a price (keeps it for history but excludes from resolution) |
PriceRepository :: async fn end_at(pool : & PgPool, id : Uuid, end_time : DateTime <Utc>) -> Result <Price> | End a price at a specific time (sets valid_to) |
async fn resolve_catalog_item_price(pool : & PgPool, item_id : Uuid, context : & PricingContext,) -> Result <ResolvedPrice> | Resolve the price for a catalog item |
async fn resolve_catalog_item_prices(pool : & PgPool, item_ids : & Uuid, context : & PricingContext,) -> Result <Vec <(Uuid, Option <ResolvedPrice>)>> | Resolve prices for multiple catalog items |
No pub use re-exports: every item above is declared in this crate.
Boundary
Reaches into foundation.
Shares tier domain with 41 other crates: domain-agreements, domain-ai-report, domain-billing, domain-catalog, domain-classify, domain-comments, domain-competitive-intel, domain-contact, … (41 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | domain |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/domain/pricing-rules |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_domain["domain"] --> n_foundation["foundation"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-money` | foundation | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
async-trait | ^0.1 | — | no | always |
chrono | ^0.4 | serde | no | always |
rust_decimal | ^1 | serde | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | no | always |
thiserror | ^2 | — | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
rust_decimal_macros | ^1 | — | no | always |
tokio | ^1 | full | no | always |
tokio-test | ^0.4 | — | no | always |
Build. None.
Depended on by. 2 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_pricing_rules["application-pricing-rules"] -->|uses| SELF n_platform_privacy_scan_api["platform-privacy-scan-api"] -->|uses| SELF SELF["domain-pricing-rules"] SELF -->|runtime| n_foundation_money["foundation-money"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | domain_pricing_rules | `src/lib.rs` |
Error model
| Error type | Named by |
|---|---|
PricingRulesError | Result |
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | none detected |
| database access | yes |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
2 workspace crates depend on this one: application-pricing-rules, platform-privacy-scan-api.
Verification
| Kind | Count |
|---|---|
| Unit tests | 5 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 10 | 0 | 3 |
What the tests establish, by name:
test_price_builder_validation—src/lib.rstest_price_specificity—src/lib.rstest_price_validity—src/lib.rstest_pricing_context_building—src/lib.rstest_resolved_price_to_money—src/lib.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 34 | 38 |
Public modules with a //! block | 0 | 0 |
pie showData
title Public items with rustdoc
"Documented" : 34
"No rustdoc detected" : 4
Metrics
| Metric | Value |
|---|---|
| Rust source files | 1 |
| Source lines | 721 |
| Code lines | 502 |
| Public API items | 38 |
| Public modules | 0 |
| Tests | 5 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 9 |
| Workspace reverse dependencies | 2 |
pie showData
title Public API by kind
"enum" : 1
"function" : 2
"method" : 28
"struct" : 6
"type alias" : 1
pie showData
title Rust source composition
"Code" : 502
"Blank or comment" : 219
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.