Contracts and traits

A contract is a Rust trait plus the types it needs. It says what a capability does. It must not know SQL, HTTP, HTML, or a concrete provider.

The framework keeps contracts small and places each one with the layer that owns it.

Traits by concern

ConcernTraitsDefined in
Application lifecycleModule, Worker, Runtime, Shutdown, TaskSpawner, Probeiet-app
Calls and validationUseCase, Validate, Session, SessionFactory, Operation, Authorizeriet-core
PersistencePersistent, TransactionSession, QueryFieldiet-core, iet-db
ConfigurationConfigSource, Configiet-config
AuthenticationAuthenticate, TokenIssuer, Csrf, ProfileStoreiet-auth
AuthorizationAuthorizer, RoleStore, AccessStoreiet-authz
CachingCacheiet-cache
Database lifecycleDatabase, Migrationsiet-db
Rate limitingRateLimitiet-rate-limit-core
Object storageobject and stream portsiet-storage
Telemetryoperation identities and recordingiet-telemetry

The exact signatures are on each crate page. This page explains why the traits are split this way.

Calls: access before validation

iet-core combines the call context, input validation, and access checks:

rust
// In a service entrypoint.
iet_core::operation::check::<CreateNote, _>(&authorizer, &context, &input).await?;

operation::check reads the operation's ACCESS, performs the identity or Authorizer step it requires, and only then calls Validate::validate.

sequenceDiagram
  participant S as "Service"
  participant K as "operation::check"
  participant A as "Authorizer"
  participant V as "Validate"
  S->>K: "check Operation input"
  K->>K: "read Operation::ACCESS"
  alt Permission
    K->>A: "authorize context permission"
    A-->>S: "AppResult"
  end
  K->>V: "validate input"
  V-->>K: "AppResult"
  K-->>S: "AppResult"

Unauthorized input is never validated, so validation cannot leak information or run side effects for a denied caller.

CallContext is claimed trust, not verified trust. Authenticate at the edge, then carry the Principal inward. See Calls and transactions for CallScope, transactions, and cancellation rules.

Persistence: contracts without SQL types

iet-db defines database lifecycle and reviewed migrations without importing driver SQL types. A provider such as iet-db-timescale implements the session and transaction traits and owns migrations.

A repository port may have an associated Session type. That lets storage require a database session without leaking a concrete database into the contract.

Dependency direction

core ← features ← integrations ← stacks ← applications
  • Core defines traits and neutral types. It selects nothing.
  • Feature crates provide one concrete mechanism, such as a transport, store, runtime, or policy.
  • Integration crates bind one transport or storage engine to a contract.
  • Stacks compose lower layers into a preset.
  • Applications select concrete types in the composition root.

A contract never imports a provider. An adapter and an implementation both depend on the contract, not on each other. See How your application is shaped for the same rule in application code.

IET Framework documentation — built with the IET Framework.