Skip to main content
Version: 2.6.x

Architecture overview

DB plugin is a flexible, extensible database abstraction layer that provides a unified interface for working with multiple database backends. It uses a data mapper pattern to separate domain objects from persistence logic, enabling powerful features like multi-database routing, caching, and real-time replication.

Core components​

Database & AbstractDatabase​

The Database interface is the central abstraction that defines all database operations. It provides methods for:

  • Reading: Query execution, counting, pagination
  • Writing: Saving, deleting, indexing
  • Transactions: Begin/commit/rollback operations
  • Configuration: Connection management, naming, utilities

Multiple database implementations can be registered and accessed by name, allowing your application to work with different backends simultaneously.

State​

State is the core data container that holds an object's field values. Think of it as a flexible map structure that:

  • Stores raw field values (primitives, collections, references)
  • Manages resolved references (lazy-loaded related objects)
  • Tracks validation errors
  • Maintains object lifecycle state (new, saved, deleted)
  • Provides path-based field access (author/name)

The State layer separates data storage from business logic, enabling powerful features like modifications and polymorphism.

Record & Recordable​

Recordable is the minimal interface for database-backed objects:

1
public interface Recordable {
2
3
State getState();
4
5
void setState(State state);
6
7
String getLabel();
8
9
<T> T as(Class<T> targetClass);
10
}

Record is the abstract base class that most domain objects extend. It provides:

  • Lifecycle hooks (beforeSave, afterSave, beforeDelete, afterDelete)
  • Validation hooks (onValidate, afterValidate)
  • Convenience methods for save/delete operations
  • Integration with the State layer

Query​

The Query class provides a fluent, type-safe API for building and executing queries. It's inspired by Apple's Cocoa Predicates and LINQ:

1
PaginatedResult<Article> articles = Query.from(Article.class)
2
.where("author/name = ?", "John Doe")
3
.and("publishDate > ?", cutoffDate)
4
.sortDescending("publishDate")
5
.select(0, 10);

Queries support predicates, sorting, pagination, grouping, and various execution modes (streaming, counting, etc.).

ObjectType​

ObjectType represents metadata about your domain types:

  • Field definitions (name, type, validation rules)
  • Index configurations
  • Type hierarchy and relationships
  • Groups for polymorphic queries
  • Source database mapping

Types are discovered and initialized at startup through the DatabaseEnvironment.

ObjectField​

ObjectField describes individual fields within a type:

  • Field name and Java type
  • Indexing configuration
  • Validation rules and annotations
  • Embedded/denormalized flags
  • Unique constraints

Architecture patterns​

Data mapper pattern​

DB plugin uses the data mapper pattern to keep domain objects independent of persistence logic:

This separation allows:

  • Domain objects to focus on business logic
  • Transparent switching between database implementations
  • Testing without database dependencies
  • Complex persistence strategies (caching, replication, federation)

Composition over inheritance (modifications)​

Modifications enable aspect-oriented composition without complex inheritance hierarchies:

Multiple objects can link to the same State, each providing different views and behaviors:

1
Article article = state.as(Article.class);
2
SEOData seo = state.as(SEOData.class); // Modification
3
SocialData social = state.as(SocialData.class); // Another modification

This pattern allows:

  • Cross-cutting concerns to be cleanly separated
  • Dynamic behavior composition at run time
  • Multiple aspects on the same object without inheritance conflicts

Lazy loading (reference resolution)​

References to other objects are stored as lightweight stubs and resolved on-demand:

Benefits:

  • Reduces initial query overhead
  • Batch resolution for N+1 query prevention
  • Configurable resolution depth
  • Support for reference-only queries

Transaction management​

Transactions use a depth-based nesting model with validation phases:

Features:

  • Nested transaction support
  • Write buffering and batching
  • Pre-write validation
  • Trigger firing after commit
  • Automatic retry on recoverable errors

Database implementations​

DB plugin supports multiple back-end implementations:

AbstractSqlDatabase (platform-sql)​

Stores objects in relational databases (MySQL, PostgreSQL, Oracle, SQL Server, etc.):

  • Automatic schema management from ObjectType metadata
  • Optimized for transactional workloads
  • Strong consistency guarantees
  • Full transaction support

SolrDatabase (platform-solr)​

Integrates with Apache Solr for full-text search:

  • Optimized for search and faceting
  • Near real-time indexing
  • Relevance scoring and boosting
  • Eventually consistent

CachingDatabase​

Wraps another database with caching layers:

  • Query result caching
  • Reference resolution caching
  • Configurable TTLs and eviction
  • Cache invalidation on writes

AggregateDatabase​

Combines multiple databases for redundancy:

  • Writes go to all databases
  • Reads from first available
  • Automatic failover
  • Consistency checking