A genuine philosophical difference, with a winner on each side. Exposed can generate the schema from code, which is excellent early on. Storm assumes the database owns the schema and verifies that your model still tells the truth about it.
Two moments in a project's life: day one, when you want tables to exist with minimal fuss, and year two, when the schema is production data under migration control and the risk is drift between what the code believes and what the database is.
Because Exposed's table objects fully describe columns and types, they can create the schema:
// The table objects can create the schema, a real convenience transaction { SchemaUtils.create(Cities, Users) }
For prototypes, tests, and greenfield day one, this is genuinely great, and Storm has no equivalent. For evolution, Exposed 1.0 adds MigrationUtils (in the exposed-migration modules), which generates the statements a schema change requires; teams typically feed those into a migration tool such as Flyway or Liquibase. The table objects remain a second description of the schema, kept in sync with the migrations by discipline.
Storm starts from the other end: the database schema, evolved by your migration tool, is the source of truth, and entities are a typed view of it. What Storm adds is verification that the view is still accurate:
// The database owns the schema; Storm checks that the model matches val orm = dataSource.orm // Inspect programmatically ... val errors: List<String> = orm.validateSchema() // ... or fail fast at startup orm.validateSchemaOrThrow()
Validation checks entities and projections against the live schema: missing tables and columns, type incompatibilities, nullability mismatches, primary key mismatches, and missing constraints. With the Spring Boot starter it runs at startup, configured by property:
# application.yml (Spring Boot starter) storm: validation: schema_mode: fail # none | warn | fail strict: true # promote drift warnings to errors, useful in CI
With strict: true in CI, a migration that quietly diverges from the model fails the build instead of failing a query in production weeks later. For tests, @StormTest runs your real schema scripts, so the same migrations that shape production shape the test database.
| Exposed | Storm | |
|---|---|---|
| Creating the schema | SchemaUtils.create(...) from table objects | Your migration tool; Storm does not generate DDL |
| Evolving the schema | MigrationUtils generates statements; a migration tool applies them | Migration tool; the model follows, verified |
| Detecting drift | Discovered when a query fails | validateSchema(), startup mode, strict CI mode |
| Source of truth | The table objects, until migrations take over | The database, always |
If your project lives mostly in the prototype phase, Exposed's generation is the nicer workflow and this page happily concedes it. Storm's bet is that schemas spend most of their lives owned by migrations, and that the valuable tool in that long phase is the one that catches drift early.
The reference documentation covers the mechanics in depth: