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}