Vault System
A Vault is a flexible data access API backed by a persistence provider. It abstracts database operations behind a consistent interface, allowing you to interact with stored data without coupling to a specific database.
Configuration
altruist:
persistence:
database:
provider: postgres
host: localhost
port: 5432
username: myuser
password: mypassword
database: mydb
cache:
provider: inmemory
The Vault Hierarchy
| Layer | Interface / Class | Description |
|---|---|---|
| Database Provider | IGeneralDatabaseProvider | Direct database interaction (CRUD) |
| Vault API | SqlVault | Abstracts queries using LINQ-like syntax |
| Injected Vault Contract | IVault<T> | The public API you use for queries, saves, updates, deletes, and history |
| Vault Registry | VaultRegistry | Runtime registry for metadata and dynamic vault lookup |
Note:
Most applications should inject IVault<T> directly. You do not need a vault repository or repository factory in normal usage.
Using Vaults
Inject IVault<T>
[Service]
public class MyService
{
private readonly IVault<PlayerVault> _playerVault;
public MyService(IVault<PlayerVault> playerVault)
{
_playerVault = playerVault;
}
}
Querying Data
var players = await _playerVault.Where(p => p.Level > 10).ToListAsync();
var player = await _playerVault.Where(p => p.Name == "Hero").FirstOrDefaultAsync();
var exists = await _playerVault.AnyAsync(p => p.Name == "Hero");
var count = await _playerVault.CountAsync();
Saving Data
await _playerVault.SaveAsync(player);
// With historical snapshot
await _playerVault.SaveAsync(player, saveHistory: true);
Updating and deleting
await _playerVault
.Where(p => p.StorageId == playerId)
.UpdateAsync(setters => setters.SetProperty(p => p.Level, p => p.Level + 1));
await _playerVault
.Where(p => p.StorageId == playerId)
.DeleteAsync();
Vault Models
Define your data models with vault attributes:
[Vault("players")]
[PrimaryKey("StorageId")]
public class Player : IVaultModel
{
public string StorageId { get; set; } = Guid.NewGuid().ToString();
[Column("name")]
public string Name { get; set; } = "";
[ColumnIndex]
public int Level { get; set; }
[Ignore]
public string TemporaryData { get; set; } = "";
}
See Unified Object Model for the full attribute reference.
Transactions
Use the [Transactional] attribute on service methods to wrap all vault operations inside a database transaction:
[Service]
public class TradeService
{
private readonly IVault<PlayerVault> _players;
private readonly IVault<ItemVault> _items;
public TradeService(IVault<PlayerVault> players, IVault<ItemVault> items)
{
_players = players;
_items = items;
}
[Transactional]
public async Task TransferItem(string fromPlayerId, string toPlayerId, string itemId)
{
var item = await _items.Where(i => i.StorageId == itemId).FirstOrDefaultAsync();
if (item == null) throw new InvalidOperationException("Item not found");
// Both operations succeed or both roll back
item.OwnerId = toPlayerId;
await _items.SaveAsync(item);
var fromPlayer = await _players.Where(p => p.StorageId == fromPlayerId).FirstOrDefaultAsync();
fromPlayer!.Gold += 100;
await _players.SaveAsync(fromPlayer);
}
}
If any operation throws an exception, the entire transaction rolls back — neither the item transfer nor the gold update persists.
You can specify the isolation level:
[Transactional(IsolationLevel.Serializable)]
public async Task CriticalOperation() { ... }
[Transactional(IsolationLevel.ReadCommitted)] // Default
public async Task NormalOperation() { ... }
Note:
[Transactional] only works on methods in [Service]-registered classes. The framework intercepts the method call to manage the transaction lifecycle.