Skip to main content

Models

Models are where database rows become application concepts. In Ormed, a model is a Dart class mapped to a table; code generation adds the tracked types, metadata, DTOs, relations, and query helpers that keep that class useful in an application.

Should this be a model?

Create a model when the data has a stable role in your application and you want to use it in more than one place:

  • The fields should be checked by the Dart compiler.
  • The entity has relationships, lifecycle rules, or reusable behavior.
  • Multiple reads and writes should share the same mapping and input rules.

Stay with direct database access when you are exploring a schema, running a one-off report, or querying a table where a domain model would only add ceremony. You can introduce a model later without changing the underlying database.

The smallest useful model

Start with the table mapping and the fields your application actually uses:

import 'package:ormed/ormed.dart';

part 'user.orm.dart';

(table: 'users')
class User extends Model<User> {
const User({
required this.id,
required this.email,
this.name,
this.createdAt,
});

(isPrimaryKey: true, autoIncrement: true)
final int id;

final String email;
final String? name;
final DateTime? createdAt;
}

Run the generator after creating or changing the model:

dart run build_runner build --delete-conflicting-outputs

The source model stays readable and intentional. Generated files hold the repetitive runtime machinery.

What gets generated

For a User model, the generated API gives you distinct types for distinct jobs:

Generated APIUse it for
$UserTracked model instances returned from queries and used in updates
UserOrmDefinitionMetadata, codecs, and lower-level model registration
UserInsertDto / UserUpdateDtoExplicit data passed into create and update operations
UserPartialQueries that select only part of a model
Users.query() / Users.where(...)Typed static query entry points

You do not need to use every generated type directly. Start with the query and repository APIs, then reach for a DTO or partial projection when the workflow benefits from being explicit.

Add model behavior when the problem appears

If you need to…Read…
Map a class, key, table, or columnDefining Models
Control input and serialized outputAttributes
Convert database values to Dart typesCasting
Connect entities and load related dataRelationships
Manage createdAt, updatedAt, or deletedAtTimestamps and Soft Deletes
Reuse query constraints or model helpersScopes and Model Methods
Observe or guard lifecycle changesEvents
Handle a database-specific type or columnDriver Overrides

This keeps a model focused: add a capability because the application needs it, not because every model has to demonstrate every feature.

A practical rollout

  1. Map the stable shape. Define the primary key, persisted fields, and any table or column overrides.
  2. Generate and verify. Run the builder, then make one read and one write through the generated API.
  3. Add boundaries deliberately. Introduce casts, fill rules, relations, timestamps, or soft deletes only when their behavior is part of the domain.
  4. Keep queries readable. Use a repository for model-oriented CRUD and the Query Builder for filtering, joins, projections, and aggregates.
  5. Test the behavior that can surprise you. Check generated mappings, relation loading, serialization, and lifecycle hooks at the edges of the application.

Continue from here