rails-architecture
npx skills add https://github.com/thibautbaissac/rails_ai_agents --skill rails-architecture
Agent 安装分布
Skill 文档
Modern Rails 8 Architecture Patterns
Overview
Rails 8 follows “convention over configuration” with a layered architecture that separates concerns. This skill guides architectural decisions for clean, maintainable code.
Architecture Decision Tree
Where should this code go?
â
ââ Is it view/display formatting?
â ââ â Presenter (see: rails-presenter skill)
â
ââ Is it complex business logic?
â ââ â Service Object (see: rails-service-object skill)
â
ââ Is it a complex database query?
â ââ â Query Object (see: rails-query-object skill)
â
ââ Is it shared behavior across models?
â ââ â Concern (see: rails-concern skill)
â
ââ Is it authorization logic?
â ââ â Policy (see: authorization-pundit skill)
â
ââ Is it reusable UI with logic?
â ââ â ViewComponent (see: viewcomponent-patterns skill)
â
ââ Is it async/background work?
â ââ â Job (see: solid-queue-setup skill)
â
ââ Is it a complex form (multi-model, wizard)?
â ââ â Form Object (see: form-object-patterns skill)
â
ââ Is it a transactional email?
â ââ â Mailer (see: action-mailer-patterns skill)
â
ââ Is it real-time/WebSocket communication?
â ââ â Channel (see: action-cable-patterns skill)
â
ââ Is it data validation only?
â ââ â Model (see: rails-model-generator skill)
â
ââ Is it HTTP request/response handling only?
ââ â Controller (see: rails-controller skill)
Layer Interaction Flow
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â REQUEST â
âââââââââââââââââââââââââââ¬ââââââââââââââââââââââââââââââââââââ
â¼
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â CONTROLLER â
â ⢠Authenticate (Authentication concern) â
â ⢠Authorize (Policy) â
â ⢠Parse params â
â ⢠Delegate to Service/Query â
ââââââââââââ¬ââââââââââââââââââââââââââââââââââ¬âââââââââââââââââ
â â
â¼ â¼
âââââââââââââââââââââââ âââââââââââââââââââââââ
â SERVICE â â QUERY â
â ⢠Business logic â â ⢠Complex queries â
â ⢠Orchestration â â ⢠Aggregations â
â ⢠Transactions â â ⢠Reports â
ââââââââââââ¬âââââââââââ ââââââââââââ¬âââââââââââ
â â
â¼ â¼
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â MODEL â
â ⢠Validations ⢠Associations ⢠Scopes ⢠Callbacks â
âââââââââââââââââââââââââââ¬ââââââââââââââââââââââââââââââââââââ
â
ââââââââââââââââ´âââââââââââââââ
â¼ â¼
âââââââââââââââââââââââ âââââââââââââââââââââââ
â PRESENTER â â VIEW COMPONENT â
â ⢠Formatting â â ⢠Reusable UI â
â ⢠Display logic â â ⢠Encapsulated â
ââââââââââââ¬âââââââââââ ââââââââââââ¬âââââââââââ
â â
ââââââââââââââââ¬âââââââââââââââ
â¼
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â RESPONSE â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ASYNC FLOWS:
âââââââââââââââââââââââ âââââââââââââââââââââââ
â JOB â â CHANNEL â
â ⢠Background work â â ⢠Real-time â
â ⢠Solid Queue â â ⢠WebSockets â
âââââââââââââââââââââââ âââââââââââââââââââââââ
EMAIL FLOWS:
âââââââââââââââââââââââ
â MAILER â
â ⢠Transactional â
â ⢠Notifications â
âââââââââââââââââââââââ
See layer-interactions.md for detailed examples.
Layer Responsibilities
| Layer | Responsibility | Should NOT contain |
|---|---|---|
| Controller | HTTP, params, response | Business logic, queries |
| Model | Data, validations, relations | Display logic, HTTP |
| Service | Business logic, orchestration | HTTP, display logic |
| Query | Complex database queries | Business logic |
| Presenter | View formatting, badges | Business logic, queries |
| Policy | Authorization rules | Business logic |
| Component | Reusable UI encapsulation | Business logic |
| Job | Async processing | HTTP, display logic |
| Form | Complex form handling | Persistence logic |
| Mailer | Email composition | Business logic |
| Channel | WebSocket communication | Business logic |
Project Directory Structure
app/
âââ channels/ # Action Cable channels
âââ components/ # ViewComponents (UI + logic)
âââ controllers/
â âââ concerns/ # Shared controller behavior
âââ forms/ # Form objects
âââ helpers/ # Simple view helpers (avoid)
âââ jobs/ # Background jobs (Solid Queue)
âââ mailers/ # Action Mailer classes
âââ models/
â âââ concerns/ # Shared model behavior
âââ policies/ # Pundit authorization
âââ presenters/ # View formatting
âââ queries/ # Complex queries
âââ services/ # Business logic
â âââ result.rb # Shared Result class
âââ views/
âââ layouts/
âââ mailer.html.erb # Email layout
Core Principles
1. Skinny Controllers
Controllers should only:
- Authenticate/authorize
- Parse params
- Call service/query
- Render response
# GOOD: Thin controller
class OrdersController < ApplicationController
def create
result = Orders::CreateService.new.call(
user: current_user,
params: order_params
)
if result.success?
redirect_to result.data, notice: t(".success")
else
flash.now[:alert] = result.error
render :new, status: :unprocessable_entity
end
end
end
# BAD: Fat controller with business logic
class OrdersController < ApplicationController
def create
@order = Order.new(order_params)
@order.user = current_user
if @order.valid?
inventory_available = @order.items.all? do |item|
Product.find(item.product_id).inventory >= item.quantity
end
if inventory_available
# ... more logic
end
end
end
end
2. Rich Models, Smart Services
Models handle:
- Validations
- Associations
- Scopes
- Simple derived attributes
Services handle:
- Multi-model operations
- External API calls
- Complex business rules
- Transactions across models
3. Result Objects for Services
All services return a consistent Result object:
class Result
attr_reader :data, :error, :code
def initialize(success:, data: nil, error: nil, code: nil)
@success = success
@data = data
@error = error
@code = code
end
def success? = @success
def failure? = !@success
end
4. Multi-Tenancy by Default
All queries scoped through account:
# GOOD: Scoped through account
def index
@events = current_account.events.recent
end
# BAD: Unscoped query
def index
@events = Event.where(user_id: current_user.id)
end
When NOT to Abstract (Avoid Over-Engineering)
| Situation | Keep It Simple | Don’t Create |
|---|---|---|
| Simple CRUD (< 10 lines) | Keep in controller | Service object |
| Used only once | Inline the code | Abstraction |
| Simple query with 1-2 conditions | Model scope | Query object |
| Basic text formatting | Helper method | Presenter |
| Single model form | form_with model: |
Form object |
| Simple partial without logic | Partial | ViewComponent |
Signs of Over-Engineering
# OVER-ENGINEERED: Service for simple save
class Users::UpdateEmailService
def call(user, email)
user.update(email: email) # Just do this in controller!
end
end
# KEEP IT SIMPLE
class UsersController < ApplicationController
def update
if @user.update(user_params)
redirect_to @user
else
render :edit
end
end
end
When TO Abstract
| Signal | Action |
|---|---|
| Same code in 3+ places | Extract to concern/service |
| Controller action > 15 lines | Extract to service |
| Model > 300 lines | Extract concerns |
| Complex conditionals | Extract to policy/service |
| Query joins 3+ tables | Extract to query object |
| Form spans multiple models | Extract to form object |
Pattern Selection Guide
Use Service Objects When:
- Logic spans multiple models
- External API calls needed
- Complex business rules
- Need consistent error handling
- Logic reused across controllers/jobs
â See rails-service-object skill for details.
Use Query Objects When:
- Complex SQL/ActiveRecord queries
- Aggregations and statistics
- Dashboard data
- Reports
â See rails-query-object skill for details.
Use Presenters When:
- Formatting data for display
- Status badges with colors
- Currency/date formatting
- Conditional display logic
â See rails-presenter skill for details.
Use Concerns When:
- Shared validations across models
- Common scopes (e.g.,
Searchable) - Shared callbacks (e.g.,
HasUuid) - Keep it single-purpose!
â See rails-concern skill for details.
Use ViewComponents When:
- Reusable UI with logic
- Complex partials
- Need testable views
- Cards, tables, badges
â See viewcomponent-patterns skill for details.
Use Form Objects When:
- Multi-model forms
- Wizard/multi-step forms
- Search/filter forms
- Contact forms (no persistence)
â See form-object-patterns skill for details.
Use Policies When:
- Resource authorization
- Role-based access
- Action permissions
- Scoped collections
â See authorization-pundit skill for details.
Rails 8 Specific Features
Authentication (Built-in Generator)
bin/rails generate authentication
Uses has_secure_password with Session model, Current class, and password reset flow.
â See authentication-flow skill for details.
Background Jobs (Solid Queue)
Database-backed job processing, no Redis required.
â See solid-queue-setup skill for details.
Real-time (Action Cable + Solid Cable)
WebSocket support with database-backed adapter.
â See action-cable-patterns skill for details.
Caching (Solid Cache)
Database-backed caching, no Redis required.
â See caching-strategies skill for details.
Other Rails 8 Defaults
| Feature | Purpose |
|---|---|
| Propshaft | Asset pipeline (replaces Sprockets) |
| Importmap | JavaScript without bundling |
| Kamal | Docker deployment |
| Thruster | HTTP/2 proxy with caching |
Anti-Patterns to Avoid
| Anti-Pattern | Problem | Solution |
|---|---|---|
| God Model | Model > 500 lines | Extract services/concerns |
| Fat Controller | Logic in controllers | Move to services |
| Callback Hell | Complex model callbacks | Use services |
| Helper Soup | Massive helper modules | Use presenters/components |
| N+1 Queries | Unoptimized queries | Use .includes(), query objects |
| Stringly Typed | Magic strings everywhere | Use constants, enums |
| Premature Abstraction | Service for 3 lines | Keep in controller |
â See performance-optimization skill for N+1 detection.
Testing Strategy by Layer
| Layer | Test Type | Focus |
|---|---|---|
| Model | Unit | Validations, scopes, methods |
| Service | Unit | Business logic, edge cases |
| Query | Unit | Query results, tenant isolation |
| Presenter | Unit | Formatting, HTML output |
| Controller | Request | Integration, HTTP flow |
| Component | Component | Rendering, variants |
| Policy | Unit | Authorization rules |
| Form | Unit | Validations, persistence |
| System | E2E | Critical user paths |
â See tdd-cycle skill for TDD workflow.
Quick Reference
New Feature Checklist
- Model – Define data structure
- Policy – Add authorization rules
- Service – Create for complex logic (if needed)
- Query – Add for complex queries (if needed)
- Controller – Keep it thin!
- Form – Use for multi-model forms (if needed)
- Presenter – Format for display
- Component – Build reusable UI
- Mailer – Add transactional emails (if needed)
- Job – Add background processing (if needed)
Refactoring Signals
| Signal | Action |
|---|---|
| Model > 300 lines | Extract concern or service |
| Controller action > 15 lines | Extract service |
| View logic in helpers | Use presenter |
| Repeated query patterns | Extract query object |
| Complex partial with logic | Use ViewComponent |
| Form with multiple models | Use form object |
| Same code in 3+ places | Extract to shared module |
Related Skills
| Category | Skills |
|---|---|
| Data Layer | rails-model-generator, rails-query-object, database-migrations |
| Business Logic | rails-service-object, rails-concern, form-object-patterns |
| Presentation | rails-presenter, viewcomponent-patterns |
| Controllers | rails-controller, api-versioning |
| Auth | authentication-flow, authorization-pundit |
| Background | solid-queue-setup, action-mailer-patterns |
| Real-time | action-cable-patterns, hotwire-patterns |
| Performance | caching-strategies, performance-optimization |
| I18n | i18n-patterns |
| Testing | tdd-cycle |
References
- See layer-interactions.md for layer communication patterns
- See service-patterns.md for service object patterns
- See query-patterns.md for query object patterns
- See error-handling.md for error handling strategies
- See testing-strategy.md for comprehensive testing