-
Notifications
You must be signed in to change notification settings - Fork 1
Architecture Overview
What this page covers: the five types you actually touch, how they fit together, and the
central idiom — capabilities are interfaces you instanceof-check, not flags.
import br.com.finalcraft.everydatabase.*;
import br.com.finalcraft.everydatabase.codec.JacksonJsonCodec;
import br.com.finalcraft.everydatabase.modules.sql.SqlConfig;
// 1. Describe the entity once: key type FIRST, entity type second.
EntityDescriptor<UUID, PlayerData> PLAYERS = EntityDescriptor.builder(UUID.class, PlayerData.class)
.collection("players")
.keyExtractor(PlayerData::getUuid)
.codec(new JacksonJsonCodec<>(PlayerData.class))
.build();
// 2. Pick a backend.
Storage storage = Storages.createSQL(new SqlConfig("jdbc:mariadb://localhost:3306/mydb", "root", "root"));
storage.init().join();
// 3. Get a typed Repository and do CRUD. Everything is async.
Repository<UUID, PlayerData> repo = storage.repository(PLAYERS);
repo.save(new PlayerData(aliceId, "Alice", 100)).join();
Optional<PlayerData> alice = repo.find(aliceId).join();
storage.close().join();Swapping createSQL(...) for createMongo(...) or createH2(...) changes nothing below
storage.repository(...). See Quick Start for the full runnable version.
📌 Every I/O call returns a
CompletableFuture. These examples use.join()for brevity; there are no blocking variants. See The Async API.
| Type | Role | You hold it… |
|---|---|---|
EntityDescriptor<K, V> |
Immutable metadata: collection name, key/entity types, keyExtractor, codec, index hints, optional version accessors. |
once per entity type, as a static final constant |
Codec<V> |
The serialization strategy (entity ↔ bytes). Attached to the descriptor. | inside the descriptor |
Storages |
The static factory. Typed builders (createSQL, createMongo, …) return concrete types; the generic create(StorageConfig) dispatches by config type. |
call once at startup |
Storage |
A live backend: lifecycle (init/close/health) + a factory for repositories. repository(...) is the only synchronous call; it caches per descriptor. |
one per backend, app-lifetime |
Repository<K, V> |
Typed CRUD: find/findMany/save/saveAll/delete/exists/count/all, plus index reads findBy/query. |
cached per descriptor by the storage |
Data flows in order: describe → build a storage → ask it for a repository → operate.
flowchart LR
ED["EntityDescriptor<K,V><br/>collection · keyExtractor · codec · indexes"]
C["Codec<V><br/>encode / decode"]
F["Storages<br/>(factory)"]
S["Storage<br/>init / close / health"]
R["Repository<K,V><br/>find / save / query …"]
DB[("backend<br/>SQL · Mongo · File · …")]
C -->|attached to| ED
F -->|create*| S
ED -->|"repository(descriptor)"| S
S -->|"repository(d)"| R
R <-->|async I/O| DB
⚠️ Gotcha —EntityDescriptor.builder(...)takes the key type first, entity type second. Both areClassobjects, so flipping them compiles but reverses<K, V>— only caught when the generics don't line up at therepository(...)call.
For the full CRUD surface see CRUD Operations; for filtered reads see Indexing & Queries. Defining Entities covers building descriptors and Codecs covers the serialization seam.
Optional features are extra interfaces a Storage may implement — discover them with
instanceof, not a boolean flag:
if (storage instanceof TransactionalStorage tx) {
tx.inTransaction(scope -> {
Repository<UUID, Account> accounts = scope.repository(ACCOUNTS);
return accounts.save(debited).thenCompose(v -> accounts.save(credited));
}).join();
}
if (storage instanceof SchemaAwareStorage schema) {
schema.register(new AddEmailColumn()).migrate().join();
}| Capability | Adds | Supported by | Not by |
|---|---|---|---|
TransactionalStorage |
inTransaction(scope → future); scope repositories share one connection, commit on success, roll back on exception or scope.rollback(). |
SQL (incl. H2), Mongo (replica set required), InMemory (no isolation) | LocalFile |
SchemaAwareStorage |
register(migrations…).migrate(); forward-only, versions tracked in a reserved _schema_migrations table/collection/file. |
SQL, Mongo, LocalFile | InMemory |
ChangeFeedStorage |
subscribe(listener) + originId(); a backend-native push feed so other instances can invalidate their caches. Wired via the manager's CacheSync. |
Mongo (Change Streams), PostgreSQL (LISTEN/NOTIFY), InMemory (local writes) |
MySQL/MariaDB, H2, LocalFile (use polling) |
Typed builders (createSQL, createPostgreSQL, etc.) return the concrete type, so when the
backend is known at compile time you can call inTransaction directly without a cast.
⚠️ Gotcha —Storages.create(StorageConfig)always picks the MySQL/MariaDB dialect for anySqlConfig. UsecreatePostgreSQL/createH2when you need those dialects.
-
Core (
everydatabase-core) — the contract types, six backends, codecs, indexing, transactions, migrations, transfer, and logging. The recommended flavor. -
Manager add-on (
everydatabase-manager) — typed references (Ref) and per-type caching managers, sitting in front of:core. See Caching & References. - Distribution flavors (core / libby) — differ only in how dependencies are packaged, not in the API. See Distribution Flavors.
- Quick Start — the full runnable end-to-end example.
-
The Async API — the
CompletableFuturemodel every call uses. - Entities, Keys & Collections — the key contract and collection rules.
- Codecs — the serialization seam.
- Defining Entities — building a descriptor in detail.
- Choosing a Backend — the capability matrix and decision guide.
- Transactions · Schema Migrations · Cross-Process Cache Sync — the capability interfaces in depth.
EveryDatabase · Home · made by Petrus Pradella
Getting Started
Core Concepts
Working with Data
Backends
Manager Module
- Caching & References
- Typed References (Ref)
- Caching Managers
- Cache Policies & Freshness
- Cross-Process Cache Sync
- One Entity, Many Databases
Operations
Advanced
Reference
Contributing