@ooneex/entity component is the foundation for domain models in the Ooneex framework. It ships the IEntity interface and the EntityClassType constructor type that every entity satisfies, so repositories, the container, and the database layer can refer to entities in a uniform, type-safe way. The package itself is intentionally tiny and dependency-free; the actual column mapping is done with TypeORM decorators on classes you generate with the CLI.
Why this component
- One contract for every entity. Each entity exposes a string
idthroughIEntity, so repositories and DI can treat any model uniformly. - Type-safe class references.
EntityClassTypetypes a constructor that produces anIEntity, letting you pass entity classes around (DI tokens, repository factories) withoutany. - TypeORM-native mapping. Columns, primary keys, timestamps, and relations are declared with standard TypeORM decorators — nothing proprietary to learn.
- Zero runtime dependencies. The package contributes only types; it adds nothing to your bundle at runtime.
- Generator-driven.
ooneex entity:createscaffolds a ready-to-edit entity plus its test file and registers it in the module.
How it works
The package surface is two declarations:| Export | Kind | Purpose |
|---|---|---|
IEntity | interface | Base shape for every entity — requires a string id. |
EntityClassType | type | Constructor type new (...args: any[]) => IEntity for passing entity classes by reference. |
@Entity, the primary key with @PrimaryColumn, plain columns with @Column, and the audit timestamps with @CreateDateColumn, @UpdateDateColumn, and @DeleteDateColumn. The id is generated up front so an instance always satisfies IEntity before it is persisted.
Usage
A minimal entity needs anid to satisfy IEntity:
EntityClassType lets helpers accept any entity class as a value:
@ManyToOne, @OneToMany, @ManyToMany, @OneToOne) alongside the columns.
Best practices
- Always give entities a string
id. SatisfyIEntityby generating the id at construction (for example withrandom.nanoid(25)), so an instance is valid before it is saved. - Map every property explicitly. Pass
name,type, andlengthto@Columnso the database schema is predictable rather than inferred. - Mark nullable columns
nullable: trueand type them| null. Keep the TypeScript type and the database constraint in sync. - Use the audit columns. Keep
@CreateDateColumn,@UpdateDateColumn, and@DeleteDateColumnfor created/updated tracking and soft deletes. - Reference classes with
EntityClassType. When passing an entity class around, type it asEntityClassTypeinstead ofFunctionorany. - Generate, then trim. Scaffold with
entity:createand remove the columns you do not need rather than starting from a blank file.
CLI command
Scaffold an entity class and its test file with the generator. It writes the class undermodules/<module>/src/entities/<Name>Entity.ts, a spec under modules/<module>/tests/entities/<Name>Entity.spec.ts, and registers the class in the module’s entities array.
| Option | Description | Default |
|---|---|---|
--name | Entity class name. Normalized to PascalCase; the Entity suffix is appended automatically. | Prompted if omitted |
--module | Target module the class is generated into. | shared |
--table-name | Database table name. | snake_case plural of the name (e.g. User → users) |
--override | Overwrite an existing entity without prompting. | false |
Use with Claude and Codex
The generator ships a matchingentity:create skill. It runs the scaffold and then guides your AI agent through completing the entity — adding the columns and relations the model needs, removing the scaffolded ones that do not apply, and finishing the test file. Initialize the skills once for your agent:
- Claude
- Codex
Prompt
entity:create --name=User, then adds email and name columns and trims the scaffolded ones.