mcpbeat

Phoenix Contexts

oliver-kriska/claude-elixir-phoenix-opencode-phoenix-contexts

Phoenix context design — creating/splitting contexts, Scope (1.8+), Ecto.Multi, PubSub, routers, plugs, controllers. Use when editing contexts, routers, or designing boundaries.

This is a copy. The original lives at oliver-kriska/claude-elixir-phoenix-phoenix-contexts.

7k tokens
context cost
the whole folder, loaded on every use
6
files
instructions only
0
copies elsewhere
how many repositories repackaged it
514
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix --skill phoenix-contexts

What comes with it

24 761 bytes besides the instruction
references/context-patterns.md
references/json-api-patterns.md
references/plug-patterns.md
references/routing-patterns.md
references/scopes-auth.md

The instruction itself

11 sections, as written by the author

Phoenix Contexts Reference

> Ash projects: Ash.Domain replaces Phoenix contexts for data access — use the ash-framework skill. 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.Multi for 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-boundary
  • references/scopes-auth.md - Scope struct, multi-tenant, authorization, plugs
  • references/routing-patterns.md - Verified routes, pipelines, API auth
  • references/plug-patterns.md - Function/module plugs, placement, guards
  • references/json-api-patterns.md - JSON controllers, FallbackController, API auth

How to use it

Copy the folder

Take oliver-kriska/claude-elixir-phoenix-opencode-phoenix-contexts from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.