Czym jest MVC? Czy Rails jest frameworkiem MVC?

Czym jest MVC? Czy Rails jest frameworkiem MVC?
MVC to jeden z najbardziej znanych wzorców architektonicznych w programowaniu i jedno z pierwszych pojęć, na jakie trafia się podczas nauki Ruby on Rails. W tym artykule wyjaśnimy, czym jest MVC, skąd się wziął, jak żądanie przechodzi przez aplikację Rails, czy Rails naprawdę można nazwać frameworkiem MVC oraz jak utrzymać porządek w każdej warstwie, gdy projekt rośnie.
Czym jest MVC?
MVC (Model–View–Controller, Model–Widok–Kontroler) to wzorzec architektoniczny, który dzieli aplikację na trzy części o różnych zadaniach:
- Model: dane i logika biznesowa. Model wie, czym jest zamówienie, użytkownik czy faktura, jakie reguły ich dotyczą i jak są przechowywane.
- Widok (View): prezentacja. Widok decyduje, jak dane wyglądają dla użytkownika: strona HTML, JSON, e-mail.
- Kontroler (Controller): koordynacja. Kontroler przyjmuje dane od użytkownika, zleca modelowi wykonanie pracy i wybiera, który widok pokazać.
Główna idea to rozdzielenie odpowiedzialności (separation of concerns): reguły biznesowe nie zależą od wyglądu strony, a strona nie decyduje, jak zapisywane są dane. Każdą część można zmieniać, testować i rozumieć osobno.
Trochę historii
MVC opisał Trygve Reenskaug w latach 1978–1979, pracując ze Smalltalkiem w Xerox PARC. Wzorzec powstał dla desktopowych interfejsów graficznych: widok subskrybował zmiany modelu i sam się przerysowywał, a kontroler obsługiwał mysz i klawiaturę.
Później frameworki webowe dostosowały tę ideę do świata HTTP z jego cyklem „żądanie–odpowiedź”. Taką webową wersję nazywa się czasem Model 2: przeglądarka wysyła żądanie, kontroler je obsługuje, pracuje z modelami i renderuje widok, a gotowa odpowiedź wraca do przeglądarki. Ruby on Rails (2004) bardzo spopularyzował to podejście, a za nim poszło wiele frameworków: Django, Laravel, ASP.NET MVC, Spring MVC, Phoenix.
Jak żądanie przechodzi przez aplikację Rails
- Przeglądarka wysyła żądanie, na przykład
GET /articles/42. - Router (
config/routes.rb) znajduje odpowiedni kontroler i akcję:ArticlesController#show. - Kontroler odczytuje parametry i prosi model o dane:
Article.find(42). - Model (Active Record) buduje zapytanie SQL, pobiera wiersz z bazy i zwraca obiekt
Article. - Kontroler przekazuje obiekt do widoku, który renderuje HTML (lub JSON).
- Odpowiedź wraca do przeglądarki.
Przyjrzyjmy się każdej części w kodzie.
Routing
# config/routes.rb
Rails.application.routes.draw do
resources :articles
end
Ta jedna linia tworzy siedem tras RESTful: index, show, new, create, edit, update i destroy.
Model
# 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
Model zawiera asocjacje, walidacje, zapytania (scope'y) i zachowanie domenowe, takie jak publish!. Nie wie nic o HTTP, sesjach ani HTML.
Kontroler
# 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
Kontroler jest cienki: odczytuje parametry, wywołuje model i decyduje, co dalej, czyli przekierować czy wyrenderować szablon. params.expect to sposób filtrowania parametrów w Rails 8; w Rails 7 to samo zapisuje się jako params.require(:article).permit(:title, :body).
Widok
<%# 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>
Widok tylko wyświetla dane otrzymane od kontrolera. Nie wykonuje z własnej inicjatywy zapytań do bazy i nie zawiera reguł biznesowych.
Czy zatem Rails jest frameworkiem MVC?
Tak. MVC to fundament Rails: foldery app/models, app/views i app/controllers, biblioteki Active Record, Action View i Action Controller oraz konwencje nazewnictwa, które je łączą, zbudowane są według tego wzorca. Oficjalne przewodniki Rails opisują go jako framework MVC.
Są jednak ważne niuanse.
1. To webowe MVC, a nie klasyczne
W oryginalnym MVC ze Smalltalka widok obserwował model i aktualizował się, gdy model się zmieniał. W Rails widok jest renderowany raz na żądanie i nie wie nic o późniejszych zmianach. Aktualizacje w czasie rzeczywistym dodaje się osobnymi narzędziami: Action Cable i Turbo Streams.
2. Model to Active Record
W Rails model to zwykle klasa, która łączy logikę biznesową z dostępem do bazy danych (wzorzec Active Record opisany przez Martina Fowlera). Jest to bardzo wygodne, ale oznacza, że logika domenowa i zapis danych żyją w tej samej klasie. W innych architekturach często się je rozdziela (np. wzorcami Data Mapper lub Repository).
3. Rails to więcej niż trzy litery
Nowoczesna aplikacja Rails zawiera wiele części, które nie mieszczą się w M, V ani C:
- Router: mapuje adresy URL na kontrolery.
- Helpery: funkcje dla widoków.
- Mailery: działają jak kontrolery dla e-maili, z własnymi widokami.
- Joby (Active Job, Solid Queue): praca w tle.
- Kanały (Action Cable): WebSockety.
- Hotwire (Turbo i Stimulus): interaktywność po stronie frontendu.
- Concerns: wspólne moduły dla modeli i kontrolerów.
Dlatego dokładniej jest powiedzieć, że Rails jest zbudowany wokół MVC i dodaje do niego wiele innych komponentów.
4. W aplikacjach API zmienia się V
Przy rails new my_api --api nie ma szablonów HTML. „Widokiem” staje się JSON budowany przez render json:, Jbuilder lub serializery, a interfejs żyje w osobnym frontendzie (React, Vue lub aplikacja mobilna). Rozdzielenie odpowiedzialności pozostaje takie samo.
Typowe problemy i ich rozwiązania
Grube kontrolery
Typowy błąd początkujących to umieszczanie logiki biznesowej w kontrolerach: obliczeń, wysyłania e-maili, wywołań zewnętrznych API. Taki kontroler trudno czytać i testować. Klasyczna rada Rails brzmi: „chudy kontroler, gruby model” (skinny controller, fat model).
Grube modele
Jeśli jednak przeniesiesz wszystko do modeli, po roku User czy Order może urosnąć do tysiąca linii. Dlatego w projektach Rails często dodaje się dodatkowe warstwy:
- Obiekty serwisowe (service objects): jedna klasa na jedną operację biznesową.
- Obiekty formularzy (form objects): formularze pracujące z kilkoma modelami naraz.
- Obiekty zapytań (query objects): złożone zapytania do bazy.
- Prezentery / dekoratory: logika wyświetlania, która nie pasuje ani do widoku, ani do modelu.
- ViewComponent lub Phlex: komponenty widoku wielokrotnego użytku, łatwe do testowania.
Przykład obiektu serwisowego, dzięki któremu kontroler pozostaje cienki:
# 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
Logika w widokach
Zapytania do bazy i złożone warunki w szablonach utrudniają ich czytanie i mogą powodować zapytania N+1. Ładuj dane w kontrolerze (przez includes), przenoś formatowanie do helperów lub prezenterów i utrzymuj szablony jak najprostsze.
MVC i jego krewni: MVP i MVVM
- MVP (Model–View–Presenter): widok jest całkowicie pasywny, a prezenter przejmuje całą logikę prezentacji. Popularny w starszych aplikacjach Android i Windows Forms.
- MVVM (Model–View–ViewModel): widok jest powiązany z modelem widoku przez wiązanie danych (data binding) i aktualizuje się automatycznie. Typowy dla Vue, Angulara, WPF i SwiftUI.
Wszystkie te wzorce mają ten sam cel co MVC: oddzielić dane i logikę biznesową od interfejsu.
Zalety i wady MVC
Zalety:
- Przejrzysta struktura: każdy wie, gdzie szukać kodu. W Rails jest to szczególnie widoczne dzięki konwencjom.
- Rozdzielenie odpowiedzialności: łatwiej zmienić interfejs bez ruszania reguł biznesowych i odwrotnie.
- Prostsze testowanie: modele, kontrolery i widoki można testować osobno.
- Równoległa praca: frontendowiec może pracować nad widokami, a backendowiec nad modelami.
Wady:
- Trzy warstwy to za mało dla dużych aplikacji, więc pojawiają się dodatkowe (serwisy, formularze, zapytania).
- Granice nie zawsze są oczywiste, a zespoły spierają się, gdzie powinien żyć kod.
- Dla bardzo małych skryptów lub jednostronicowych narzędzi MVC może być przesadą.
Dobre praktyki MVC w Rails
- Utrzymuj cienkie kontrolery: parametry, autoryzacja, wywołanie modelu lub serwisu i odpowiedź.
- Umieszczaj reguły biznesowe w modelach lub serwisach, a nie w kontrolerach i widokach.
- Utrzymuj proste widoki: żadnych zapytań ani złożonej logiki; używaj helperów, partiali i komponentów.
- Trzymaj się tras RESTful: siedem standardowych akcji na zasób. Jeśli potrzebujesz niestandardowej akcji, rozważ nowy zasób lub kontroler.
- Przestrzegaj konwencji Rails dotyczących nazw i folderów: to na nich opiera się cała „magia”.
- Dodawaj nowe warstwy tylko wtedy, gdy są potrzebne: obiekt serwisowy na każde dwie linie kodu to także przekombinowanie.
- Pilnuj zapytań N+1: używaj
includesw kontrolerze lub w scope'ach modelu.
Podsumowanie
MVC dzieli aplikację na trzy części: model przechowuje dane i reguły biznesowe, widok pokazuje je użytkownikowi, a kontroler łączy je, obsługując żądania. Rails zdecydowanie jest frameworkiem MVC: cała struktura projektu zbudowana jest wokół tego wzorca. Jest to jednak webowa wersja MVC z modelami opartymi na Active Record, a Rails dodaje do niej routing, joby, mailery, Hotwire i wiele więcej. Zrozumienie MVC pomaga wiedzieć, gdzie powinien trafić każdy fragment kodu, a znajomość jego ograniczeń pozwala w porę dodać odpowiednie warstwy, gdy aplikacja rośnie.
Wstecz



