Skip to main content

Unveiling TeaQL Architecture: Declarative Multi-Destination Data Service Routing — Farewell to the 'Glue Code' of Polyglot Persistence

· 5 min read
TeaQL Team
Core Team

In modern complex application architectures, polyglot persistence has almost become a standard configuration. Within a single system, core transactional data may require PostgreSQL for strong consistency guarantees, massive execution logs might need Meilisearch for full-text search, state information could be thrown into Redis for ultra-high-speed caching, and local development might even downgrade to rusqlite.

However, while enjoying the ultimate performance of multi-destination storage, we often find ourselves trapped in a painful "code swamp."

1. The Pain Points: The "Schizophrenia" of Multiple Data Sources and AI Disasters

If you implement such polyglot persistence using traditional architectures, your business code will typically be littered with scenarios like this:

// Traditional business logic Frankenstein
if entity.is_task() {
pg_pool.execute("INSERT INTO task...", &task).await?;
} else if entity.is_execution_log() {
meilisearch_client.index("task_execution_log").add_documents(&log).await?;
}

We have to configure multiple connection pools, introduce multiple ORMs or SDKs, and even write hundreds of lines of IF/ELSE blocks in the business logic just to keep the data synchronized.

In the era of AI-assisted programming (Agentic Coding), this architecture is extremely unfriendly to AI. If an AI Agent needs to navigate such a codebase, it must simultaneously master the API syntax of multiple underlying databases. With the slightest mistake, it will mix up Postgres syntax with the Meilisearch SDK, generating disastrous code hallucinations. Not to mention, when the architecture evolves and you want to migrate an entity from MySQL to Elasticsearch, the related business logic often faces the risk of being completely scrapped and rewritten.

We need an ultimate solution designed for Agents.

2. TeaQL's Solution: The "Underscore Philosophy" and "Inheritance Mechanism"

In the TeaQL framework, we adhere strictly to the philosophy of "Model as the Single Source of Truth". How do we elegantly resolve routing across multiple data services? We compress all the complexity into the system core, leaving only a simple multiple-choice question for developers (or AI) in the XML model file.

Let's look at how a TeaQL model defines the destination of data storage:

<!-- 1. Global configuration: Define the overall data_service on the root node. The entire domain defaults to rusqlite. -->
<root name="robot-kanban-service" org="doublechaintech" data_service="rusqlite">

<!-- 2. Default inheritance: No data service declared here; automatically inherits rusqlite from root to go to relational storage. -->
<task
name="string()"
status="string()"
/>

<!-- 3. Local override: Business requires logs to support full-text search, so we explicitly switch to meilisearch via _data_service. -->
<task_execution_log
action="string()"
detail="string()"
_data_service="meilisearch"
/>

</root>

Why the "Underscore"?

Hidden here is a core syntax design of the TeaQL parser: distinguishing business data fields from framework metadata.

  • Attributes without an underscore (such as name="string()" or data_service on the root node) are treated by the parser as business-level entity data fields (Fields).
  • Attributes prefixed with an underscore (such as _data_service or _audit_mask_fields) represent metadata (Metadata) that controls framework behavior.

If we wrote data_service="meilisearch" without the underscore on <task_execution_log>, the parser would assume that the task has a business field named data_service! Through this ultra-minimal underscore convention, we achieve complete decoupling of business and framework configuration without breaking the purity of the XML at all.

3. Architectural Magic: Transparent Dispatching Under the Hood

When the generator scans the model above, it reads _data_service and passes the context to the STG templates. The final entity code generated on the Rust side looks like this:

#[derive(Clone, Debug, PartialEq, TeaqlEntity)]
// The macro is injected with data_service = "meilisearch"
#[teaql(entity = "TaskExecutionLog", table = "task_execution_log_data", data_service = "meilisearch")]
pub struct TaskExecutionLog { ... }

What magic does this bring to the business-layer code? No matter where the underlying tables actually reside, the business-layer developers and AI see only one interface: a unified, elegant TeaQL domain-specific language API.

// Business code: Saving a task automatically routes to rusqlite under the hood
let mut task = Task::new("Deploy App");
ctx.save(&task).await?;

// Business code: Saving a log automatically invokes the Meilisearch Provider SDK under the hood
let mut log = TaskExecutionLog::new("System start");
ctx.save(&log).await?;

No "glue code", no ugly conditional branches. Through the underlying Rust Trait mechanism (Providers), ctx.save() acts like a smart mail sorting station, accurately dispatching data to its proper place based on the data_service label attached to each entity's head.

4. The Pinnacle of Agentic Coding: Reducing Cognitive Load

Under an architecture like TeaQL:

  1. Preventing Human Error: Business developers will never write data to the wrong database due to mistakenly calling the wrong connection pool.
  2. Unleashing AI Potential: When generating complex business logic flows, AI no longer needs to read the API manuals of various data components.

We have split "architectural decision-making" and "business coding" into their most logical stages. During the modeling phase, the AI or architect determines the physical layer direction with a single _data_service attribute; during the coding phase, no matter what business logic code is generated, the underlying layer maintains absolute consistency and security.

A truly excellent modern framework must not only serve humans but also understand how to elegantly guide AI collaboration. TeaQL is a pioneering work born exactly from this philosophy.