5.3 KiB
Framework vs Application Ownership
This document defines ownership boundaries between the framework and application code. These boundaries are intentional and non-negotiable: they exist to preserve upgradeability, predictability, and developer sanity under stress.
Ownership answers a simple question:
Who is allowed to change this, and under what rules?
The framework draws a hard line between framework‑owned and application‑owned concerns, while still encouraging extension through explicit, visible mechanisms.
Core Principle
The framework is not a library of suggestions. It is a runtime with invariants.
Application code:
- uses the framework
- extends it through defined seams
- never mutates or overrides its invariants
Framework code:
- guarantees stable behavior
- owns critical lifecycle and security concerns
- must remain internally consistent across versions
Breaking this boundary creates systems that work until they don’t, usually during upgrades or emergencies.
Database Ownership
Framework‑Owned Tables
Certain database tables are owned and managed exclusively by the framework.
Examples (illustrative, not exhaustive):
- authentication primitives
- session or token state
- internal capability/permission metadata
- migration bookkeeping
- framework feature flags or invariants
Rules
Application code must not:
- modify schema
- add columns
- delete rows
- update rows directly
- rely on undocumented columns or behaviors
Application code may:
- read via documented framework APIs
- reference stable identifiers explicitly exposed by the framework
Think of these tables as private internal state — even though they live in your database.
If the framework needs you to interact with this data, it will expose an API for it.
Rationale
These tables:
- encode security or correctness invariants
- may change structure across framework versions
- must remain globally coherent
Treating them as app‑owned data tightly couples your app to framework internals and blocks safe upgrades.
Application‑Owned Tables
All domain data belongs to the application.
Examples:
- users (as domain actors, not auth primitives)
- posts, orders, comments, invoices
- business‑specific joins and projections
- denormalized or performance‑oriented tables
Rules
Application code:
- owns schema design
- owns migrations
- owns constraints and indexes
- may evolve these tables freely
The framework:
- never mutates application tables implicitly
- interacts only through explicit queries or contracts
Integration Pattern
Where framework concepts must relate to app data:
- use foreign keys to framework‑exposed identifiers, or
- introduce explicit join tables owned by the application
No hidden coupling, no magic backfills.
Code Ownership
Framework‑Owned Code
Some classes, constants, and modules are framework‑owned.
These include:
- core request/response abstractions
- auth and user primitives
- capability/permission evaluation logic
- lifecycle hooks
- low‑level utilities relied on by the framework itself
Rules
Application code must not:
- modify framework source
- monkey‑patch or override internals
- rely on undocumented behavior
- change constant values or internal defaults
Framework code is treated as read‑only from the app’s perspective.
Extension Is Encouraged (But Explicit)
Ownership does not mean rigidity.
The framework is designed to be extended via intentional seams, such as:
- subclassing
- composition
- adapters
- delegation
- configuration objects
- explicit registration APIs
Preferred Patterns
- Subclass when behavior is stable and conceptual
- Compose when behavior is contextual or optional
- Delegate when authority should remain with the framework
What matters is that extension is:
- visible in code
- locally understandable
- reversible
No spooky action at a distance.
What the App Owns Completely
The application fully owns:
- domain models and data shapes
- SQL queries and result parsing
- business rules
- authorization policy inputs (not the engine)
- rendering decisions
- feature flags specific to the app
- performance trade‑offs
The framework does not attempt to infer intent from your domain.
What the Framework Guarantees
In return for respecting ownership boundaries, the framework guarantees:
- stable semantics across versions
- forward‑only migrations for its own tables
- explicit deprecations
- no silent behavior changes
- identical runtime behavior in dev and prod
The framework may evolve internally — but never by reaching into your app’s data or code.
A Useful Mental Model
- Framework‑owned things are constitutional law
- Application‑owned things are legislation
You can write any laws you want — but you don’t amend the constitution inline.
If you need a new power, the framework should expose it deliberately.
Summary
- Ownership is about who is allowed to change what
- Framework‑owned tables and code are read‑only to the app
- Application‑owned tables and code are sovereign
- Extension is encouraged, mutation is not
- Explicit seams beat clever hacks
Respecting these boundaries keeps systems boring — and boring systems survive stress.