From layered-rails
Guides Rails developers applying The Extended Rails Way: gradually extracting abstraction layers (service objects, query objects, form objects, presenters, policy objects, and more) to keep growing Rails codebases maintainable. Use when reviewing Rails code for layered architecture violations, identifying God object models, extracting patterns from fat controllers or fat models, or planning progressive refactoring of an existing Rails application.
How this skill is triggered — by the user, by Claude, or both
Slash command
/layered-rails:layered-railsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Rails' MVC conventions are a strength — they compress complexity and accelerate early development. **The Extended Rails Way** builds on that foundation by gradually extracting new abstraction layers when Rails' two-abstraction model (controllers + models) starts to buckle under scale. This plugin helps you recognize when extraction is warranted, choose the right pattern, and execute the refacto...
references/anti-patterns/catalog.mdreferences/core/active-record-models.mdreferences/core/layered-architecture-principles.mdreferences/core/rails-way-extended.mdreferences/patterns/adapters-and-wrappers.mdreferences/patterns/authorization-layer.mdreferences/patterns/configuration-objects.mdreferences/patterns/filter-objects.mdreferences/patterns/form-objects.mdreferences/patterns/notifications-layer.mdreferences/patterns/presenters-and-serializers.mdreferences/patterns/query-objects-and-repositories.mdreferences/patterns/service-objects.mdreferences/patterns/view-components.mdreferences/topics/callbacks-concerns-globals.mdreferences/topics/infrastructure-and-cross-layers.mdRails' MVC conventions are a strength — they compress complexity and accelerate early development. The Extended Rails Way builds on that foundation by gradually extracting new abstraction layers when Rails' two-abstraction model (controllers + models) starts to buckle under scale. This plugin helps you recognize when extraction is warranted, choose the right pattern, and execute the refactoring safely.
Key distinction: an abstraction layer is a reusable code concept within a single horizontal division; an architecture layer (Presentation, Application/Services, Domain, Infrastructure) is the broader division itself. Never let an abstraction cross an architecture layer boundary.
Use this table to orient a piece of logic before reaching for a pattern.
| If the code… | Architecture layer | Likely pattern | Reference |
|---|---|---|---|
| Renders or formats data for a view | Presentation | Presenter, View Component | presenters-and-serializers, view-components |
| Serializes data for an API response | Presentation | Serializer | presenters-and-serializers |
| Filters/scopes a collection for display | Presentation → Domain | Filter Object | filter-objects |
| Validates and processes a form submission | Application/Services | Form Object | form-objects |
| Orchestrates a multi-step business operation | Application/Services | Service Object | service-objects |
| Encapsulates a complex query or scope | Domain | Query Object / Repository | query-objects-and-repositories |
| Checks whether a user can perform an action | Domain | Policy Object | authorization-layer |
| Sends email, push, or other notifications | Above Domain | Delivery Object | notifications-layer |
| Wraps external service or ENV configuration | Infrastructure | Configuration Object, Adapter | configuration-objects, adapters-and-wrappers |
| Doesn't fit any layer cleanly yet | Application/Services | app/services waiting room | service-objects |
| Pattern | One-line description | Reference |
|---|---|---|
| Service Object | Callable object for a single business operation between controllers and models | service-objects |
| Query Object | Encapsulates a complex query; #resolve returns a relation | query-objects-and-repositories |
| Form Object | Active Model wrapper for multi-model or conditional form submissions | form-objects |
| Filter Object | Separates presentation-layer filtering (Rubanok) from domain query objects | filter-objects |
| Presenter | Open/closed decorator for view-specific model logic | presenters-and-serializers |
| Serializer | Specialized presenter for API output | presenters-and-serializers |
| Policy Object | Named predicate object for authorization checks (RBAC or ABAC) | authorization-layer |
| Delivery Object | Notification abstraction above the domain layer (Active Delivery / Noticed) | notifications-layer |
| View Component | Explicit-interface UI component replacing partials (ViewComponent gem) | view-components |
| Configuration Object | First-class settings object via Anyway Config; separates settings from secrets | configuration-objects |
| Adapter / Wrapper | Isolates infrastructure dependencies; wrapper is a degenerate adapter | adapters-and-wrappers |
| Anti-pattern | Layer violated | One-line remediation | Full catalog |
|---|---|---|---|
| God Object model | Domain | Run churn×complexity; extract service objects or query objects | catalog |
| Callback accumulation (score > 2) | Domain | Score callbacks 0–5; extract to event-driven subscribers | catalog, callbacks-concerns-globals |
| Fat Controller | Application/Services | Move business logic to service objects; leave only context-building | catalog |
| Anemic Model | Domain | Re-absorb trivial delegations; only extract genuine cross-concern logic | catalog |
| Presentation logic in models | Presentation | Extract to presenter or view component | catalog |
| Inline authorization in controllers | Domain | Extract to policy object | catalog |
| ENV hell / raw ENV access | Infrastructure | Introduce configuration object (Anyway Config) | catalog |
Overcrowded app/services | Application/Services | Apply specification test; route to granular abstraction layer | catalog |
| Architecture sinkhole | Any | Collapse pass-through layers; only add a layer when it adds behavior | catalog |
| Reverse dependency | Any | Invert the dependency; lower layers must not reference higher layers | catalog |
/identify-god-objects or ask the layered-rails-reviewer agent/extract-query-object/extract-form-object/extract-presenter/extract-policy-object/extract-callback-to-event or read callbacks-concerns-globals/extract-view-component/introduce-config-object/spec-test| Command | When to use |
|---|---|
/identify-god-objects | A model file is large or frequently changing; you want churn×complexity data |
/spec-test | Unsure whether a class is doing too much; enumerate responsibilities without writing tests |
/extract-query-object | Complex scopes or multi-join queries live in a model or controller |
/extract-form-object | Conditional validations or cross-model callbacks are tied to a specific form |
/extract-presenter | View-specific formatting methods are accumulating on a model |
/extract-policy-object | Authorization logic is scattered across controllers or service objects |
/extract-callback-to-event | A model callback scores ≥ 3 on the 0–5 callback scoring framework |
/introduce-config-object | A service reads multiple ENV keys directly; settings and secrets are mixed |
/extract-view-component | A partial or helper has grown complex enough to need an explicit interface |
| File | Description |
|---|---|
| layered-architecture-principles | Single responsibility per layer, loose coupling, no reverse dependencies, architecture sinkhole avoidance |
| rails-way-extended | The Rails Way vs The Extended Rails Way; convention-over-configuration as foundation; conceptual compression |
| active-record-models | Active Record responsibilities, the God object problem, churn×complexity analysis |
| File | Description |
|---|---|
| service-objects | Callable interface, fat controller → fat model → service refactoring path, anemic model warning |
| query-objects-and-repositories | #resolve convention, naming, model namespace placement, parameterized modules |
| form-objects | Active Model foundation, #save API contract, attribute declarations, multi-model submissions |
| filter-objects | Rubanok, separating filter objects (presentation) from query objects (domain) |
| presenters-and-serializers | Open/closed decorator, multi-model presenters/facades, serializers as specialized presenters |
| authorization-layer | RBAC vs ABAC, policy object naming and predicates, enforcement in controllers |
| notifications-layer | Mailer placement, Active Delivery and Noticed frameworks, delivery objects |
| view-components | ViewComponent gem, explicit interfaces vs partials, strict locals |
| configuration-objects | Settings vs secrets, ENV hell, Anyway Config, configuration object pattern |
| adapters-and-wrappers | Wrapper as degenerate adapter, multi-environment flexibility, plugin pattern |
| File | Description |
|---|---|
| callbacks-concerns-globals | Callback scoring framework (0–5), transforming vs observing callbacks, concerns, global state |
| infrastructure-and-cross-layers | Rails.logger tagged logging, Rails.error, Active Support instrumentation, cross-layer concerns |
| File | Description |
|---|---|
| catalog | Full catalog organized by layer: model, controller, presentation, infrastructure |
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub mcrundo/layered-rails