Phoenix Contexts Reference
SkillMediaPhoenix context design — creating/splitting contexts, Scope (1.8+), Ecto.Multi,
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Phoenix Contexts Reference skill
What this skill tells your AI
The instructions your AI receives, as published by oliver-kriska/claude-elixir-phoenix in targets/pi/skills/phoenix-contexts/SKILL.md and read by ahel’s review.
Ash projects:
Ash.Domainreplaces Phoenix contexts for data access — use theash-frameworkskill. Context boundary and PubSub patterns still apply.
Reference for designing and implementing Phoenix contexts (bounded contexts).
Iron Laws — Never Violate These
- CONTEXTS OWN THEIR DATA — Never query another context's schema directly via Repo
- SCOPES ARE MANDATORY (Phoenix 1.8+) — Every context function MUST accept scope as first parameter
- THIN CONTROLLERS/LIVEVIEWS — Controllers translate HTTP, business logic stays in contexts
- NO SIDE EFFECTS IN SCHEMAS — Use
Ecto.Multifor transactions with side effects
Context Structure
lib/my_app/
├── accounts/ # Context directory
│ ├── user.ex # Schema
│ ├── scope.ex # Scope struct (Phoenix 1.8+)
├── accounts.ex # Context module (public API)
Phoenix 1.8+ Scopes (CRITICAL)
All context functions MUST accept scope as first parameter:
def list_posts(%Scope{} = scope) do
from(p in Post, where: p.user_id == ^scope.user.id)
|> Repo.all()
end
def create_post(%Scope{} = scope, attrs) do
%Post{user_id: scope.user.id}
|> Post.changeset(attrs)
|> Repo.insert()
|> broadcast(scope, :created)
end
Quick Decisions
When to SPLIT contexts?
- Module exceeds ~400 lines
- Functions don't share domain language
- Could theoretically be a separate microservice
- Team member could own it independently
When to KEEP together?
- Resources share vocabulary and domain concepts
- Functions frequently operate on same data together
- Splitting would create excessive cross-context calls
Cross-Context References
# ✅ Reference by ID, convert at boundary
def create_order(%Scope{} = scope, user_id, product_ids) do
with {:ok, user} <- Accounts.fetch_user(scope, user_id) do
do_create_order(scope, user.id, product_ids)
end
end
# ❌ Reaching into other context's internals
alias MyApp.Accounts.User # Don't do this
Repo.all(from o in Order, join: u in User, ...) # Don't query other schemas
Anti-patterns
| Wrong | Right |
|---|---|
Service objects (UserCreationService) | Context functions (Accounts.create_user/2) |
| Repository pattern wrapping Repo | Repo IS the repository |
| Direct Repo calls in controllers | Delegate to context |
| Schema callbacks with side effects | Use Ecto.Multi |
Version Notes
- Phoenix 1.8+: Uses built-in
%Scope{}struct for authorization context - Phoenix 1.7: Requires manual authorization context (see
references/scopes-auth.md"Pre-Scopes Patterns")
References
For detailed patterns, see:
references/context-patterns.md- Full context module, PubSub, Multi, cross-boundaryreferences/scopes-auth.md- Scope struct, multi-tenant, authorization, plugsreferences/routing-patterns.md- Verified routes, pipelines, API authreferences/plug-patterns.md- Function/module plugs, placement, guardsreferences/json-api-patterns.md- JSON controllers, FallbackController, API auth
Signals
- GitHub stars
- 543
- Forks
- 38
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
phoenix-contexts- Source
- github.com/oliver-kriska/claude-elixir-phoenix