$npx -y skills add ThibautBaissac/rails_ai_agents --skill rails-architectureGuides modern Rails 8 code architecture decisions and patterns. Use when deciding where to put code, choosing between patterns (service objects vs concerns vs query objects), designing feature architecture, refactoring for better organization, or when user mentions architecture,
| 1 | # Modern Rails 8 Architecture Patterns |
| 2 | |
| 3 | ## Architecture Decision Tree |
| 4 | |
| 5 | ``` |
| 6 | Where should this code go? |
| 7 | | |
| 8 | +- View/display formatting? -> Presenter (@presenter-agent) |
| 9 | +- Complex business logic? -> Service Object (@service-agent) |
| 10 | +- Complex database query? -> Query Object (@query-agent) |
| 11 | +- Shared behavior across models? -> Concern (/rails-concern skill) |
| 12 | +- Authorization logic? -> Policy (@policy-agent) |
| 13 | +- Reusable UI with logic? -> ViewComponent (@viewcomponent-agent) |
| 14 | +- Async/background work? -> Job (@job-agent, /solid-queue-setup skill) |
| 15 | +- Complex form (multi-model)? -> Form Object (@form-agent) |
| 16 | +- Transactional email? -> Mailer (@mailer-agent) |
| 17 | +- Real-time/WebSocket? -> Channel (/action-cable-patterns skill) |
| 18 | +- Data validation only? -> Model (@model-agent) |
| 19 | +- HTTP request/response only? -> Controller (@controller-agent) |
| 20 | ``` |
| 21 | |
| 22 | ## Layer Responsibilities |
| 23 | |
| 24 | | Layer | Responsibility | Should NOT contain | |
| 25 | |-------|---------------|-------------------| |
| 26 | | **Controller** | HTTP, params, response | Business logic, queries | |
| 27 | | **Model** | Data, validations, relations | Display logic, HTTP | |
| 28 | | **Service** | Business logic, orchestration | HTTP, display logic | |
| 29 | | **Query** | Complex database queries | Business logic | |
| 30 | | **Presenter** | View formatting, badges | Business logic, queries | |
| 31 | | **Policy** | Authorization rules | Business logic | |
| 32 | | **Component** | Reusable UI encapsulation | Business logic | |
| 33 | | **Job** | Async processing | HTTP, display logic | |
| 34 | | **Form** | Complex form handling | Persistence logic | |
| 35 | | **Mailer** | Email composition | Business logic | |
| 36 | | **Channel** | WebSocket communication | Business logic | |
| 37 | |
| 38 | ## When NOT to Abstract |
| 39 | |
| 40 | | Situation | Keep It Simple | Don't Create | |
| 41 | |-----------|----------------|--------------| |
| 42 | | Simple CRUD (< 10 lines) | Keep in controller | Service object | |
| 43 | | Used only once | Inline the code | Abstraction | |
| 44 | | Simple query with 1-2 conditions | Model scope | Query object | |
| 45 | | Basic text formatting | Helper method | Presenter | |
| 46 | | Single model form | `form_with model:` | Form object | |
| 47 | | Simple partial without logic | Partial | ViewComponent | |
| 48 | |
| 49 | ## When TO Abstract |
| 50 | |
| 51 | | Signal | Action | |
| 52 | |--------|--------| |
| 53 | | Same code in 3+ places | Extract to concern/service | |
| 54 | | Controller action > 15 lines | Extract to service | |
| 55 | | Model > 300 lines | Extract concerns | |
| 56 | | Complex conditionals | Extract to policy/service | |
| 57 | | Query joins 3+ tables | Extract to query object | |
| 58 | | Form spans multiple models | Extract to form object | |
| 59 | |
| 60 | See /extraction-timing skill for detailed extraction guidance. |
| 61 | |
| 62 | ## Core Patterns |
| 63 | |
| 64 | ### Skinny Controllers |
| 65 | |
| 66 | ```ruby |
| 67 | # GOOD: Thin controller delegates to service |
| 68 | class OrdersController < ApplicationController |
| 69 | def create |
| 70 | result = Orders::CreateService.call(user: current_user, params: order_params) |
| 71 | if result.success? |
| 72 | redirect_to result.data, notice: t(".success") |
| 73 | else |
| 74 | flash.now[:alert] = result.error |
| 75 | render :new, status: :unprocessable_entity |
| 76 | end |
| 77 | end |
| 78 | end |
| 79 | ``` |
| 80 | |
| 81 | ### Result Objects for Services |
| 82 | |
| 83 | All services return a consistent Result object: |
| 84 | |
| 85 | ```ruby |
| 86 | Result = Data.define(:success, :data, :error) do |
| 87 | def success? = success |
| 88 | def failure? = !success |
| 89 | end |
| 90 | ``` |
| 91 | |
| 92 | ### Multi-Tenancy by Default |
| 93 | |
| 94 | ```ruby |
| 95 | # GOOD: Scoped through account |
| 96 | def index |
| 97 | @events = current_account.events.recent |
| 98 | end |
| 99 | ``` |
| 100 | |
| 101 | ## Rails 8 Specific Features |
| 102 | |
| 103 | | Feature | Purpose | Skill/Agent | |
| 104 | |---------|---------|-------------| |
| 105 | | Authentication | `has_secure_password` generator | /authentication-flow | |
| 106 | | Background Jobs | Solid Queue (database-backed) | /solid-queue-setup, @job-agent | |
| 107 | | Real-time | Action Cable + Solid Cable | /action-cable-patterns | |
| 108 | | Caching | Solid Cache (database-backed) | /caching-strategies | |
| 109 | | Assets | Propshaft + Import Maps | (built-in) | |
| 110 | | Deployment | Kamal 2 + Thruster | (built-in) | |
| 111 | |
| 112 | ## Testing Strategy by Layer |
| 113 | |
| 114 | | Layer | Test Type | Focus | |
| 115 | |-------|-----------|-------| |
| 116 | | Model | Unit | Validations, scopes, methods | |
| 117 | | Service | Unit | Business logic, edge cases | |
| 118 | | Query | Unit | Query results, tenant isolation | |
| 119 | | Presenter | Unit | Formatting, HTML output | |
| 120 | | Controller | Request | Integration, HTTP flow | |
| 121 | | Component | Component | Rendering, variants | |
| 122 | | Policy | Unit | Authorization rules | |
| 123 | | System | E2E | Critical user paths | |
| 124 | |
| 125 | ## New Feature Checklist |
| 126 | |
| 127 | 1. **Model** - Define data structure (@migration-agent, @model |