220 lines
5.3 KiB
Markdown
220 lines
5.3 KiB
Markdown
# 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.
|