03-Core-Interfaces.md 12 KB

Core Interfaces

This document defines the fundamental interfaces that form Implexus's public API.

Async I/O Model

All I/O operations in Implexus are asynchronous. Methods that require database access use Vala's async keyword and must be called with yield. This ensures the main loop remains responsive during database operations.

Key principles:

  • Identity properties (path, name, entity_type, engine) are synchronous - no I/O required
  • Navigation, CRUD, and query operations are async - use *_async methods
  • Hooks run synchronously in the DBM worker thread context
  • Results are returned to the main loop via Idle.add()

EntityType Enum

Defines the four types of entities in the database.

namespace Implexus.Core {

public enum EntityType {
    CONTAINER,
    DOCUMENT,
    CATEGORY,
    INDEX;
    
    public string to_string() {
        switch (this) {
            case CONTAINER: return "container";
            case DOCUMENT: return "document";
            case CATEGORY: return "category";
            case INDEX: return "index";
            default: assert_not_reached();
        }
    }
    
    public static EntityType? from_string(string name) {
        switch (name.down()) {
            case "container": return CONTAINER;
            case "document": return DOCUMENT;
            case "category": return CATEGORY;
            case "index": return INDEX;
            default: return null;
        }
    }
}

} // namespace Implexus.Core

Entity Interface

The base interface for all database entities. All I/O operations are async.

namespace Implexus.Core {

public interface Entity : Object {
    
    // === Identity (Synchronous - No I/O) ===
    public abstract unowned Engine engine { get; }
    public abstract Path path { owned get; }
    public abstract string name { owned get; }
    public abstract EntityType entity_type { get; }
    public abstract string type_label { owned get; }
    public abstract string configured_expression { owned get; }
    public abstract string configured_type_label { owned get; }
    
    // === Parent/Child Navigation (Async) ===
    public abstract async Entity? get_parent_async() throws EntityError;
    public abstract async Invercargill.ReadOnlySet<string> get_child_names_async() throws EntityError;
    public abstract async Entity? get_child_async(string name) throws EntityError;
    public abstract async Entity[] get_children_async() throws EntityError;
    
    // === Child Management (CONTAINER only - Async) ===
    public abstract async Entity? create_container_async(string name) throws EntityError;
    public abstract async Entity? create_document_async(string name, string type_label) throws EntityError;
    public abstract async Entity? create_category_async(
        string name, 
        string type_label, 
        string expression
    ) throws EntityError;
    public abstract async Entity? create_index_async(
        string name, 
        string type_label, 
        string expression
    ) throws EntityError;
    public abstract async Entity? create_catalogue_async(
        string name,
        string type_label,
        string expression
    ) throws EntityError;
    
    // === Document Operations (DOCUMENT only - Async) ===
    public abstract async Invercargill.Properties get_properties_async() throws EntityError;
    public abstract async Invercargill.Element? get_entity_property_async(string name) throws EntityError;
    public abstract async void set_entity_property_async(string name, Invercargill.Element value) throws EntityError;
    public abstract async void remove_property_async(string name) throws EntityError;
    
    // === Lifecycle (Async) ===
    public abstract async void delete_async() throws EntityError;
    public abstract bool exists { get; }
    
    // === Set Operations (Async) ===
    public abstract async EntitySet as_set_async();
    
    // === Signals ===
    public signal void property_changed(string key);
}

} // namespace Implexus.Core

Engine Interface

Unified API for both embedded and remote operation modes. All I/O operations are async.

namespace Implexus.Core {

public interface Engine : Object {
    
    // === Root Access (Async) ===
    public abstract async Entity get_root_async() throws EngineError;
    
    // === Path-Based Access (Async) ===
    public abstract async Entity? get_entity_async(Path path) throws EngineError;
    public abstract async Entity? get_entity_or_null_async(Path path) throws EngineError;
    public abstract async bool entity_exists_async(Path path) throws EngineError;
    
    // === Query Operations (Async) ===
    public abstract async Entity[] query_by_type_async(string type_label) throws EngineError;
    public abstract async Entity[] query_by_expression_async(
        string type_label, 
        string expression
    ) throws EngineError;
    
    // === Transactions (Async) ===
    public abstract async Transaction begin_transaction_async() throws EngineError;
    public abstract async void commit_async() throws EngineError;
    public abstract async void rollback_async();
    public abstract bool in_transaction { get; }
    
    // === Configuration (Synchronous) ===
    public abstract StorageConfiguration configuration { owned get; }
    
    // === Events ===
    public signal void entity_created(Entity entity);
    public signal void entity_deleted(Path path);
    public signal void entity_modified(Entity entity);
}

} // namespace Implexus.Core

Transaction Management

Vala doesn't support async delegates, so there's no with_write_transaction() helper. Use manual begin/commit/rollback:

try {
    yield engine.begin_transaction_async();
    // perform operations
    yield engine.commit_async();
} catch (Error e) {
    yield engine.rollback_async();
    throw e;
}

Storage Interface

Entity persistence abstraction.

namespace Implexus.Storage {

public interface Storage : Object {
    
    // Entity Operations
    public abstract bool has_entity(Path path);
    public abstract uint8[]? load_entity(Path path);
    public abstract void save_entity(Path path, uint8[] data) throws StorageError;
    public abstract void delete_entity(Path path) throws StorageError;
    
    // Child Tracking
    public abstract Invercargill.ReadOnlySet<string> get_child_names(Path parent_path);
    public abstract void register_child(Path parent, string child_name);
    public abstract void unregister_child(Path parent, string child_name);
    
    // Indexing Support
    public abstract Invercargill.Enumerable<Path> get_paths_by_type(string type_label);
    public abstract void register_type(Path path, string type_label);
    public abstract void unregister_type(Path path, string type_label);
    
    // Transactions
    public abstract void begin_transaction() throws StorageError;
    public abstract void commit_transaction() throws StorageError;
    public abstract void rollback_transaction();
    public abstract bool in_transaction { get; }
    
    // Lifecycle
    public abstract void open() throws StorageError;
    public abstract void close();
    public abstract void compact() throws StorageError;
}

} // namespace Implexus.Storage

DBM Interface

Low-level key-value storage interface with concurrent read support indicator.

namespace Implexus.Storage {

public interface Dbm : Object {
    
    // === Concurrent Read Support ===
    /**
     * Whether this DBM supports concurrent read operations.
     * - LMDB: true (MVCC allows concurrent readers)
     * - GDBM: false (single-threaded access required)
     * - Filesystem: false (single-threaded access required)
     */
    public abstract bool supports_concurrent_reads { get; }
    
    // === Basic Operations ===
    public abstract bool has_key(string key);
    public abstract Invercargill.BinaryData? @get(string key);
    public abstract void @set(string key, Invercargill.BinaryData value) throws StorageError;
    public abstract void delete(string key) throws StorageError;
    
    // === Iteration ===
    public abstract Invercargill.Enumerable<string> keys { owned get; }
    
    // === Transactions ===
    public abstract void begin_transaction() throws StorageError;
    public abstract void commit_transaction() throws StorageError;
    public abstract void rollback_transaction();
    public abstract bool in_transaction { get; }
}

} // namespace Implexus.Storage

Transaction Interface

Transaction support for atomic operations.

namespace Implexus.Core {

public interface Transaction : Object {
    
    public abstract bool active { get; }
    public abstract async void commit_async() throws EngineError;
    public abstract async void rollback_async();
}

} // namespace Implexus.Core

Error Domains

EngineError

namespace Implexus.Core {

public errordomain EngineError {
    ENTITY_NOT_FOUND,
    ENTITY_ALREADY_EXISTS,
    INVALID_PATH,
    INVALID_OPERATION,
    TYPE_MISMATCH,
    EXPRESSION_ERROR,
    TRANSACTION_ERROR,
    STORAGE_ERROR,
    CONNECTION_ERROR,
    PROTOCOL_ERROR;
}

} // namespace Implexus.Core

EntityError

namespace Implexus.Core {

public errordomain EntityError {
    NOT_FOUND,
    ALREADY_EXISTS,
    INVALID_OPERATION,
    TYPE_MISMATCH,
    STORAGE_ERROR,
    IO_ERROR;
}

} // namespace Implexus.Core

StorageError

namespace Implexus.Storage {

public errordomain StorageError {
    OPEN_ERROR,
    READ_ERROR,
    WRITE_ERROR,
    DELETE_ERROR,
    TRANSACTION_ERROR,
    CORRUPTION_ERROR,
    DISK_FULL,
    IO_ERROR;
}

} // namespace Implexus.Storage

Interface Relationships

graph TB
    subgraph Core
        Entity[Entity Interface]
        Engine[Engine Interface]
        Transaction[Transaction Interface]
        EntityType[EntityType Enum]
    end
    
    subgraph Storage
        Storage[Storage Interface]
        Dbm[Dbm Interface]
        AsyncDbmQueue[AsyncDbmQueue]
        DbmOperation[DbmOperation]
    end
    
    Engine --> Entity
    Engine --> Transaction
    Engine --> Storage
    Storage --> Dbm
    Storage --> AsyncDbmQueue
    AsyncDbmQueue --> DbmOperation
    Entity --> EntityType
    Entity --> Engine

Usage Patterns

Accessing Entities

// Via path (async)
var entity = yield engine.get_entity_async(new Path("/users/john"));

// Via navigation (async)
var root = yield engine.get_root_async();
var users = yield root.get_child_async("users");
var john = yield users.get_child_async("john");

Creating Entities

// Create container (async)
var container = yield (yield engine.get_root_async()).create_container_async("app");

// Create document (async)
var doc = yield container.create_document_async("config", "AppConfig");
yield doc.set_entity_property_async("version", new ValueElement("1.0.0"));

// Create category - auto-categorize documents by property value (async)
var category = yield container.create_category_async(
    "by_status",
    "Task",
    "status"  // Expression to evaluate on each Task document
);

// Create index - text search on documents (async)
var index = yield container.create_index_async(
    "search",
    "Task",
    "description"  // Expression to search within
);

Using Transactions

try {
    yield engine.begin_transaction_async();
    var container = yield (yield engine.get_root_async()).create_container_async("batch");
    var doc = yield container.create_document_async("item1", "Item");
    yield doc.set_entity_property_async("value", new ValueElement(42));
    
    var doc2 = yield (yield engine.get_root_async()).get_child_async("batch");
    doc2 = yield ((!) doc2).create_document_async("item2", "Item");
    yield doc2.set_entity_property_async("value", new ValueElement(84));
    
    yield engine.commit_async();
} catch (Error e) {
    yield engine.rollback_async();
    warning("Transaction failed: %s", e.message);
}

Querying Entities

// Query all entities of a type (async)
var tasks = yield engine.query_by_type_async("Task");
foreach (var task in tasks) {
    var status = yield task.get_entity_property_async("status");
    message("Task status: %s", status?.to_string() ?? "null");
}

// Query with expression filter (async)
var activeTasks = yield engine.query_by_expression_async("Task", "status == 'active'");