Що таке MVC? Чи є Rails MVC-фреймворком?

Що таке MVC? Чи є Rails MVC-фреймворком?
MVC — один із найвідоміших архітектурних патернів у розробці та один із перших термінів, з якими стикаєшся під час вивчення Ruby on Rails. У цій статті розберемо, що таке MVC, звідки він з'явився, як запит проходить через Rails-застосунок, чи можна справді назвати Rails MVC-фреймворком і як тримати кожен шар у чистоті в міру зростання проєкту.
Що таке MVC?
MVC (Model–View–Controller, Модель–Представлення–Контролер) — це архітектурний патерн, який ділить застосунок на три частини з різними обов'язками:
- Модель (Model) — дані та бізнес-логіка. Модель знає, що таке замовлення, користувач чи рахунок, які правила до них застосовуються і як вони зберігаються.
- Представлення (View) — відображення. Представлення вирішує, як дані виглядають для користувача: HTML-сторінка, JSON, лист.
- Контролер (Controller) — координація. Контролер приймає введення користувача, доручає моделі виконати роботу та обирає, яке представлення показати.
Головна ідея — розділення відповідальності (separation of concerns): бізнес-правила не залежать від того, як виглядає сторінка, а сторінка не вирішує, як зберігаються дані. Кожну частину можна змінювати, тестувати й розуміти окремо.
Трохи історії
MVC описав Трюгве Реенскауг у 1978–1979 роках, коли працював зі Smalltalk у Xerox PARC. Патерн створювався для настільних графічних інтерфейсів: представлення підписувалося на зміни моделі й саме перемальовувалося, а контролер обробляв введення з миші та клавіатури.
Пізніше вебфреймворки адаптували цю ідею до світу HTTP з його циклом «запит–відповідь». Таку веб-версію іноді називають Model 2: браузер надсилає запит, контролер його обробляє, працює з моделями та рендерить представлення, а готова відповідь надсилається назад. Ruby on Rails (2004) зробив цей підхід дуже популярним, і за ним пішли багато фреймворків: Django, Laravel, ASP.NET MVC, Spring MVC, Phoenix.
Як запит проходить через Rails-застосунок
- Браузер надсилає запит, наприклад
GET /articles/42. - Роутер (
config/routes.rb) знаходить відповідний контролер і дію:ArticlesController#show. - Контролер читає параметри й запитує дані в моделі:
Article.find(42). - Модель (Active Record) будує SQL-запит, отримує рядок із бази та повертає об'єкт
Article. - Контролер передає об'єкт у представлення, яке рендерить HTML (або JSON).
- Відповідь повертається в браузер.
Подивімося на кожну частину в коді.
Маршрутизація
# config/routes.rb
Rails.application.routes.draw do
resources :articles
end
Цей один рядок створює сім RESTful-маршрутів: index, show, new, create, edit, update і destroy.
Модель
# app/models/article.rb
class Article < ApplicationRecord
belongs_to :author, class_name: "User"
has_many :comments, dependent: :destroy
validates :title, presence: true, length: { maximum: 200 }
validates :body, presence: true
scope :published, -> { where.not(published_at: nil) }
def publish!
update!(published_at: Time.current)
end
def reading_time
(body.split.size / 200.0).ceil
end
end
Модель містить зв'язки, валідації, запити (скоупи) та поведінку предметної області, наприклад publish!. Вона нічого не знає про HTTP, сесії чи HTML.
Контролер
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
before_action :set_article, only: %i[show edit update destroy]
def index
@articles = Article.published.order(published_at: :desc)
end
def show
end
def create
@article = Current.user.articles.build(article_params)
if @article.save
redirect_to @article, notice: "Article created"
else
render :new, status: :unprocessable_entity
end
end
private
def set_article
@article = Article.find(params[:id])
end
def article_params
params.expect(article: [:title, :body])
end
end
Контролер тонкий: він читає параметри, викликає модель і вирішує, що робити далі — зробити редирект чи відрендерити шаблон. params.expect — спосіб фільтрації параметрів у Rails 8; у Rails 7 те саме пишеться як params.require(:article).permit(:title, :body).
Представлення
<%# app/views/articles/show.html.erb %>
<article>
<h1><%= @article.title %></h1>
<p class="meta">
<%= @article.author.name %> · <%= @article.reading_time %> min read
</p>
<%= simple_format(@article.body) %>
<%= render @article.comments %>
</article>
Представлення лише відображає дані, отримані від контролера. Воно не виконує запитів до бази з власної ініціативи й не містить бізнес-правил.
То чи є Rails MVC-фреймворком?
Так. MVC — це основа Rails: папки app/models, app/views і app/controllers, бібліотеки Active Record, Action View та Action Controller і угоди щодо іменування, які пов'язують їх разом, побудовані за цим патерном. Офіційні посібники Rails описують його як MVC-фреймворк.
Але є кілька важливих нюансів.
1. Це веб-MVC, а не класичний MVC
В оригінальному MVC зі Smalltalk представлення спостерігало за моделлю й оновлювалося при її зміні. У Rails представлення рендериться один раз на запит і нічого не знає про подальші зміни. Оновлення в реальному часі додаються окремими інструментами: Action Cable і Turbo Streams.
2. Модель — це Active Record
У Rails модель зазвичай — це клас, який поєднує бізнес-логіку та доступ до бази даних (патерн Active Record, описаний Мартіном Фаулером). Це дуже зручно, але означає, що логіка предметної області та зберігання даних живуть в одному класі. В інших архітектурах їх часто розділяють (наприклад, патернами Data Mapper або Repository).
3. Rails — це більше, ніж три літери
Сучасний Rails-застосунок містить багато частин, які не вкладаються в M, V чи C:
- Роутер — зіставляє URL із контролерами.
- Хелпери — функції для представлень.
- Мейлери — працюють як контролери для листів, зі своїми представленнями.
- Джоби (Active Job, Solid Queue) — фонова робота.
- Канали (Action Cable) — WebSockets.
- Hotwire (Turbo і Stimulus) — інтерактивність на фронтенді.
- Concerns — спільні модулі для моделей і контролерів.
Тому точніше сказати, що Rails побудований навколо MVC і додає поверх нього багато інших компонентів.
4. В API-застосунках змінюється V
При rails new my_api --api HTML-шаблонів немає. «Представленням» стає JSON, який будується через render json:, Jbuilder або серіалізатори, а інтерфейс живе в окремому фронтенді (React, Vue чи мобільний застосунок). Розділення відповідальності при цьому зберігається.
Типові проблеми та їх розв'язання
Товсті контролери
Типова помилка новачків — класти бізнес-логіку в контролери: розрахунки, надсилання листів, виклики зовнішніх API. Такий контролер важко читати й тестувати. Класична порада Rails — «тонкий контролер, товста модель» (skinny controller, fat model).
Товсті моделі
Але якщо переносити все в моделі, через рік User чи Order може розростися до тисячі рядків. Тому в Rails-проєктах часто додають додаткові шари:
- Сервісні об'єкти (service objects) — один клас на одну бізнес-операцію.
- Об'єкти форм (form objects) — форми, що працюють одразу з кількома моделями.
- Об'єкти запитів (query objects) — складні запити до бази.
- Презентери / декоратори — логіка відображення, якій не місце ні в представленні, ні в моделі.
- ViewComponent або Phlex — перевикористовувані та тестовані компоненти представлення.
Приклад сервісного об'єкта, який залишає контролер тонким:
# app/services/orders/checkout.rb
module Orders
class Checkout
def initialize(order, payment_gateway: PaymentGateway.new)
@order = order
@payment_gateway = payment_gateway
end
def call
Order.transaction do
@payment_gateway.charge!(@order.total, @order.user)
@order.update!(status: :paid, paid_at: Time.current)
end
OrderMailer.with(order: @order).confirmation.deliver_later
@order
end
end
end
# app/controllers/checkouts_controller.rb
class CheckoutsController < ApplicationController
def create
order = Current.user.orders.find(params[:order_id])
Orders::Checkout.new(order).call
redirect_to order, notice: "Thank you for your order!"
rescue PaymentGateway::Error => e
redirect_to order, alert: e.message
end
end
Логіка в представленнях
Запити до бази та складні умови в шаблонах роблять їх важкими для читання й можуть спричиняти N+1 запити. Завантажуйте дані в контролері (через includes), виносьте форматування в хелпери чи презентери та тримайте шаблони максимально простими.
MVC та його родичі: MVP і MVVM
- MVP (Model–View–Presenter) — представлення повністю пасивне, а презентер бере на себе всю логіку відображення. Популярний у старих Android- та Windows Forms-застосунках.
- MVVM (Model–View–ViewModel) — представлення пов'язане з моделлю представлення через прив'язку даних (data binding) і оновлюється автоматично. Типовий для Vue, Angular, WPF і SwiftUI.
Усі ці патерни мають ту саму мету, що й MVC: відокремити дані та бізнес-логіку від інтерфейсу.
Плюси та мінуси MVC
Плюси:
- Зрозуміла структура: усі знають, де шукати код. У Rails це особливо відчутно завдяки угодам.
- Розділення відповідальності: простіше змінювати інтерфейс, не чіпаючи бізнес-правил, і навпаки.
- Просте тестування: моделі, контролери та представлення можна тестувати окремо.
- Паралельна робота: фронтенд-розробник може працювати з представленнями, поки бекенд-розробник працює з моделями.
Мінуси:
- Трьох шарів замало для великих застосунків, тому з'являються додаткові (сервіси, форми, запити).
- Межі не завжди очевидні, і команди сперечаються, де має жити код.
- Для зовсім маленьких скриптів чи односторінкових утиліт MVC може бути надмірним.
Найкращі практики MVC у Rails
- Тримайте контролери тонкими: параметри, авторизація, виклик моделі чи сервісу та відповідь.
- Розміщуйте бізнес-правила в моделях або сервісах, а не в контролерах і представленнях.
- Робіть представлення простими: жодних запитів і складної логіки; використовуйте хелпери, партіали та компоненти.
- Дотримуйтеся RESTful-маршрутів: сім стандартних дій на ресурс. Якщо потрібна нестандартна дія, подумайте про новий ресурс чи контролер.
- Дотримуйтеся угод Rails щодо імен і папок: саме на них працює «магія».
- Додавайте нові шари лише за потреби: сервісний об'єкт на кожні два рядки коду — це теж переускладнення.
- Стежте за N+1 запитами: використовуйте
includesу контролері або в скоупах моделі.
Висновок
MVC ділить застосунок на три частини: модель зберігає дані та бізнес-правила, представлення показує їх користувачеві, а контролер пов'язує їх, обробляючи запити. Rails, безумовно, MVC-фреймворк: уся структура проєкту побудована навколо цього патерну. Водночас це веб-версія MVC з моделями на Active Record, і Rails додає до неї маршрутизацію, джоби, мейлери, Hotwire та багато іншого. Розуміння MVC допомагає знати, де місце кожному шматку коду, а розуміння його меж — вчасно додавати потрібні шари в міру зростання застосунку.
Назад



