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 |
| Vault Factory | VaultFactory | Creates the correct vault instance |
| Vault Repository | VaultRepository | Groups vaults by keyspace |
Note:
Most users only interact with VaultRepository directly.
Using Vaults
Inject a VaultRepository
[Service]
public class MyService
{
private readonly IVault<Player> _playerVault;
public MyService(IVault<Player> 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();
Saving Data
await _playerVault.SaveAsync(player);
// With historical snapshot
await _playerVault.SaveAsync(player, storeHistory: true);
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.