Фон
Я пытаюсь создать базу данных PostgreSQL, в которой будут храниться торговые записи с бирж криптовалюты. У меня есть одна Trade
модель в моем клиенте, но я хотел бы разделить сделки на их собственные таблицы на основе символа, который они представляют. Доступные символы выводятся во время выполнения, и каждый из сделок с биржи имеет свою собственную последовательность Id, начинающуюся с 0.
Например, скажем, я хочу кэшировать сделки из Binance. Мой клиент вызывал бы их API, видя, что (некоторые) символы, доступные на платформе, являются BTCUSDT
, ETHUSDT
и ETHBTC
, и если эти символы еще не находятся в таблице bn.Symbols
, это будет вставить их, а также создать новую таблицу bn.Trades-SYMBOLNAME
.
Я использую технический стек .NET Core 3 с Dapper и PostgreSQL.
Выпуск
Я видел несколько постов, подробно описывающих, как написать процедуру или функцию, которая создает таблицу с предоставленной переменной в качестве имени, и похоже, что это плохой дизайн .
В дополнение к этому негативному настроению, мой клиент использует шаблоны репозитория и единицы работы, и TradeRepository
взаимодействует таким образом, что определяет метод GetById(long)
, поэтому я не могу указать, какой символ мне нужен в репозитории. к цели, и я не могу указать символ в функциях репозитория, не прибегая к некоторым грязным трюкам.
Простым решением было бы сохранить все сделки в одной таблице, иметь столбец внешнего ключа для Symbol
и создать уникальный составной индекс для столбцов Id
и Symbol
. Однако я не решаюсь сделать это, потому что в конечном итоге я буду хранить более миллиарда сделок, и этот подход также все еще вызывает проблемы с моим текущим дизайном шаблона репозитория.
Есть ли лучший способ приблизиться к этому?
Код
Вот код, показывающий, как настроены мои модели:
public abstract class EntityBase : IEntity
{
#region Properties
public long Id
{
get;
set;
}
#endregion
}
public class Symbol : EntityBase
{
#region Data Members
private long _baseAssetId;
private long _quoteAssetId;
#endregion
#region Properties
public string Name
{
get;
set;
}
public long BaseAssetId
{
get
{
if( BaseAsset != null )
return _baseAssetId = BaseAsset.Id;
return _baseAssetId;
}
set => _baseAssetId = value;
}
public int BaseAssetPrecision
{
get;
set;
}
public long QuoteAssetId
{
get
{
if( QuoteAsset != null )
return _quoteAssetId = QuoteAsset.Id;
return _quoteAssetId;
}
set => _quoteAssetId = value;
}
public int QuoteAssetPrecision
{
get;
set;
}
#endregion
#region Navigation Properties
public virtual Asset BaseAsset
{
get;
set;
}
public virtual Asset QuoteAsset
{
get;
set;
}
#endregion
}
public class Trade : EntityBase
{
#region Properties
public decimal Price
{
get;
set;
}
public decimal Quantity
{
get;
set;
}
public decimal QuoteQuantity
{
get;
set;
}
public long Timestamp
{
get;
set;
}
public bool IsBuyer
{
get;
set;
}
public DateTime Time
{
get => Timestamp.ToUnixTimeMilliseconds();
}
#endregion
}
Вот как все хранилища связаны:
public interface IRepository<TEntity> : IDisposable
where TEntity : IEntity, new()
{
Task AddAsync( TEntity entity, CancellationToken cancellationToken = default );
Task<TEntity> GetByIdAsync( long id, CancellationToken cancellationToken = default );
Task<bool> RemoveAsync( TEntity entity, CancellationToken cancellationToken = default );
Task<bool> UpdateAsync( TEntity entity, CancellationToken cancellationToken = default );
}
А вот как интерфейс UnitOfWork:
public interface IUnitOfWork : IDisposable
{
#region Properties
Guid Id
{
get;
}
IDbConnection Connection
{
get;
}
IDbTransaction Transaction
{
get;
}
#endregion
#region Repositories
IAssetRepository Assets
{
get;
}
ITradeRepository Trades
{
get;
}
ISymbolRepository Symbols
{
get;
}
#endregion
#region Public Methods
void Begin();
void Commit();
void Rollback();
#endregion
}