Repositories
@di-framework/repo provides a coherent abstraction of repositories and storage adapters, allowing you to decouple your business logic from the underlying storage technology. It integrates seamlessly with @di-framework/core.
Key Concepts
Storage Adapter
A StorageAdapter is a minimal protocol that every storage backend must implement. It keeps the repository layer agnostic to whether you are using SQL, NoSQL, In-Memory, or an external API.
Repository
The Repository layer uses a StorageAdapter to perform data operations. It can add business logic, caching, validation, or event dispatching.
BaseRepository<E, ID>: The foundational repository class.EntityRepository<E, ID>: A standard entity-aware repository.SoftDeleteRepository<E, ID>: AddssoftDelete,restore, and filtering for active/deleted records.
Installation
Important: Scoped imports
Always import from the scoped package @di-framework/core/* to ensure a single global container instance. Mixing different import IDs (e.g., di-framework/* or relative paths to sources) can load a second copy of the library and create a second global container instance.
Correct:
Avoid:
Usage with @di-framework/core
Define a model with Spring/JPA-style @Model, @Id, and optional @GeneratedValue (the class is the type), then register a repository with @Repository.
IdKind covers multi-context identity (Primary, Public, External, Legacy, Tenant, Version). Multiple Primary fields express a composite primary key. @GeneratedValue stacks with @Id like JPA; GenerationType matches Jakarta (Auto, Identity, Sequence, Table, UUID), and UUID means UUIDv7 in this framework.
Plain interfaces still work if you do not need model metadata. Read metadata with getModelMetadata/getIdentities/getPrimaryId/isModel. Foreign keys to other models are not @Id kinds.
Injecting Repositories
Once registered, you can inject your repository into any other container-managed class:
Built-in In-Memory Repository
For prototyping, testing, or simple local state, use InMemoryRepository:
Custom Adapters
You can implement your own adapter to connect to any data source:
Database migrations
@di-framework/repo discovers and applies SQLite schema migrations from decorated classes, SQL files, and a JSON manifest. The three sources merge; they are not mutually exclusive.
Decorator
@Migration requires version and description. binding defaults to 'default'. Registration happens at import time. The runner constructs new target() with no arguments — constructor DI is not used. up is required at execution; execute/run are accepted aliases. down is stored on the definition but MigrationRunner.execute() never calls it. There is no rollback CLI.
SQL files and JSON manifests
SQL discovery (discoverSqlMigrations(dir)): *.sql except *.down.sql. Version and description come from header comments or the filename (001_create_users.sql, V2__add_index.sql). Split bodies with -- migrate:up/-- migrate:down.
Manifest discovery (discoverManifestMigrations) reads JSON only (not TOML):
entry.file is resolved relative to the manifest directory. Optional directory merges SQL files; the same (version, binding) from the directory is skipped.
Runner
History lives in _migrations keyed by (version, binding). A lock table _migrations_lock serializes runners (60s steal timeout). Each successful up runs inside db.transaction(...); a thrown up rolls back and does not write history.
Versions: integers compare numerically; strings with ., _, or - compare by dotted segments (1.2 < 1.10). Duplicate versions throw MigrationIntegrityError. A pending version lower than the latest applied throws MigrationOrderError.
Checksums: SQL/manifest use sha256(upSql); decorator checksums use Class.toString() (native vs minified Wasm output can disagree).
autoApply() applies pending migrations only when NODE_ENV is development or test, unless enabled: true. Nothing in application bootstrap calls it except tests. Actor SQLite databases reuse this runner on activation with binding equal to the actor type. See Actors.
CLI
Flag | Behavior |
|---|---|
| SQLite path. Default: |
| SQL directory (default |
| JSON manifest (default |
| Default |
| Repeatable; import |
| Execute only: max pending migrations |
| Execute only: plan, do not apply |
JSON data: status { binding, isUpToDate, applied[], pending[] }; execute { binding, applied[], pending[], dryRun, durationMs }. Binding mismatch exits 2. Connect or runner failures exit 1.
Limitations
downis parsed and stored; it is never executed by the runner or CLISQLite cannot fully roll back some DDL even inside a transaction
No baseline API, no repeatable migrations, no constructor-injected
DatabasewasmCloud does not scan
@Migrationclasses at build time; actor migrations run in-guest before activation