fts_core/
ports.rs

1//! Interface traits for the flow trading system.
2//!
3//! This module contains the "ports" in the hexagonal architecture pattern.
4//!
5//! These traits define the contract between the domain logic and external adapters
6//! (such as databases, APIs, or other services) without specifying implementation details.
7//! This separation allows for easier testing and the ability to swap out infrastructure
8//! components without affecting the core business logic.
9
10use std::hash::Hash;
11
12mod product;
13pub use product::ProductRepository;
14
15mod demand;
16pub use demand::DemandRepository;
17
18mod portfolio;
19pub use portfolio::PortfolioRepository;
20
21mod batch;
22pub use batch::BatchRepository;
23
24mod solver;
25pub use solver::Solver;
26
27/// A base trait for defining the fundamental data- and error-types.
28///
29/// This trait establishes the core type system used throughout the repositories.
30/// Implementations define concrete types for identifiers, timestamps, and errors,
31/// allowing the system to work with different backends (SQL, NoSQL, etc.).
32///
33/// There is no requirement or material advantage to breaking this trait across
34/// multiple orthogonal traits as we have, but Rust does not allow partially
35/// implementing a trait across multiple files. By breaking this trait apart,
36/// this allows an implementation to keep the logic separated and ease
37/// development.
38pub trait Repository {
39    /// The error type for underlying operations
40    type Error: std::error::Error;
41
42    /// A type suitable for expressing a timestamp
43    type DateTime;
44
45    /// A type representing a bidder id
46    type BidderId: Eq + Hash;
47
48    /// A type representing a demand id
49    type DemandId: Eq + Hash;
50
51    /// A type representing a portfolio id
52    type PortfolioId: Eq + Hash;
53
54    /// A type representing a product id
55    type ProductId: Eq + Hash;
56}
57
58/// Application-level configuration and integration point.
59///
60/// While the Repository traits are agnostic as to the DemandData,
61/// PortfolioData, and ProductData, an Application must specify concrete types.
62/// Additionally, an application must provide a clock, id generators, and the
63/// permissioning logic.
64///
65/// This trait serves as the main integration point between the generic
66/// flow trading system and a specific application's requirements.
67pub trait Application {
68    /// An authorization context
69    type Context;
70
71    /// Application-specific data to associate to a demand curve
72    type DemandData;
73
74    /// Application-specific data to associate to a demand curve
75    type PortfolioData;
76
77    /// Application-specific data to associate to a demand curve
78    type ProductData;
79
80    /// The underlying implementation for data operations
81    type Repository: DemandRepository<Self::DemandData>
82        + PortfolioRepository<Self::PortfolioData>
83        + ProductRepository<Self::ProductData>
84        + BatchRepository<Self::Solver>;
85
86    /// The solver to use for executing auctions
87    type Solver: Solver<
88            <Self::Repository as Repository>::DemandId,
89            <Self::Repository as Repository>::PortfolioId,
90            <Self::Repository as Repository>::ProductId,
91        >;
92
93    /// Get the application's repository
94    fn database(&self) -> &Self::Repository;
95
96    /// Get an instance of the solver
97    fn solver(&self) -> Self::Solver;
98
99    /// Get the current time
100    fn now(&self) -> <Self::Repository as Repository>::DateTime;
101
102    /// Generate an appropriate id for the provided demand data
103    fn generate_demand_id(
104        &self,
105        data: &Self::DemandData,
106    ) -> <Self::Repository as Repository>::DemandId;
107
108    /// Generate an appropriate id for the provided portfolio data
109    fn generate_portfolio_id(
110        &self,
111        data: &Self::PortfolioData,
112    ) -> <Self::Repository as Repository>::PortfolioId;
113
114    /// Generate an appropriate id for the provided product data
115    fn generate_product_id(
116        &self,
117        data: &Self::ProductData,
118    ) -> <Self::Repository as Repository>::ProductId;
119
120    /// Check if the context can create new demands or portfolios.
121    ///
122    /// Returns the bidder ID to use for the new entity if authorized,
123    /// or None if the operation is not permitted.
124    fn can_create_bid(
125        &self,
126        context: &Self::Context,
127    ) -> impl Future<Output = Option<<Self::Repository as Repository>::BidderId>> + Send;
128
129    /// Get the list of bidders that the context is authorized to query.
130    ///
131    /// Returns a vector of bidder IDs that the context can access.
132    /// An empty vector means no query access is granted.
133    fn can_query_bid(
134        &self,
135        context: &Self::Context,
136    ) -> impl Future<Output = Vec<<Self::Repository as Repository>::BidderId>> + Send;
137
138    /// Check if the context can read bid data for a specific bidder.
139    ///
140    /// This controls access to demand and portfolio data owned by the bidder.
141    fn can_read_bid(
142        &self,
143        context: &Self::Context,
144        bidder_id: <Self::Repository as Repository>::BidderId,
145    ) -> impl Future<Output = bool> + Send;
146
147    /// Check if the context can update bid data for a specific bidder.
148    ///
149    /// This controls the ability to modify demands and portfolios owned by the bidder.
150    fn can_update_bid(
151        &self,
152        context: &Self::Context,
153        bidder_id: <Self::Repository as Repository>::BidderId,
154    ) -> impl Future<Output = bool> + Send;
155
156    /// Check if the context can view or query product information.
157    fn can_view_products(&self, context: &Self::Context) -> impl Future<Output = bool> + Send;
158
159    /// Check if the context can manage products (create, partition).
160    fn can_manage_products(&self, context: &Self::Context) -> impl Future<Output = bool> + Send;
161
162    /// Check if the context can execute batch auctions.
163    fn can_run_batch(&self, context: &Self::Context) -> impl Future<Output = bool> + Send;
164}