Réteges architektúra anélkül, hogy beleásnánk magunkat a túltervezés örvényébe
Sziasztok!
Gondolom, te is találkoztál már azzal a helyzettel, amikor egy új projekt előtt állva a tervezési fázis végtelenbe nyúló vitába fullad: hány réteg legyen? Melyik keretrendszert válasszuk? És ha később mégis másra lesz szükség? A végén olyan bonyolult architektúrát rajzolunk ki, mintha egy banki rendszert építenénk, holott egy egyszerű alkalmazásról van szó. Ma arról szeretnék beszélni, hogyan lehet egy tiszta, réteges backend architektúrát kialakítani anélkül, hogy beleesnénk a túltervezés csapdájába. Célunk, hogy a clean code elveit tiszteletben tartva hozzunk létre valami jól karbantarthatót, de ne végezzünk felesleges munkát.
Mi az a „réteges architektúra” valójában?
A lényeg egyszerű: a réteges architektúra (vagy layered architecture) arról szól, hogy a felelősségeket elkülönítjük. Nem rakod össze a HTML generálást az adatbázis-lekérdezéssel egy függvényben. Inkább létrehozol egy logikai réteget, ami az üzleti logikát kezeli, egy adatelérési réteget, ami az adatbázissal beszél, és egy prezentációs réteget, ami a felhasználói felületért felel. Ez nem egy dogma, hanem egy eszköz a kód rendszerezésére és a változtatások egyszerűsítésére.
A nagy veszély itt a túltervezés: amikor már az első napon mikroservicereket, üzenetsorokat és eseményvezérelt architektúrát tervezünk egy olyan alkalmazáshoz, ami kezdetben csak egy egyszerű CRUD műveleteket végez. A kulcs az, hogy kezdd egyszerűen, de tiszta határokkal.
Egy gyakorlati példa: Egyszerű blogmotor
Képzelj el egy alap blogmotort, ahol felhasználók megjeleníthetik a bejegyzéseket. Hogyan építenénk ezt meg rétegesen, de közben nem bonyolítjuk túl?
1. Adatelérési réteg (Repository)
Ez a réteg egyedül az adatbázissal kommunikál. Semmi üzleti logika, csak lekérdezések és adatmódosítások.
// repository/PostRepository.php
class PostRepository {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
public function findPublishedPosts(): array {
$stmt = $this->db->prepare("SELECT * FROM posts WHERE status = 'published' ORDER BY created_at DESC");
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function findById(int $id): ?array {
$stmt = $this->db->prepare("SELECT * FROM posts WHERE id = :id");
$stmt->execute([':id' => $id]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result ? $result : null;
}
}2. Üzleti logikai réteg (Service)
Itt található az alkalmazás „agyá”. Ez a réteg használja a Repository-t, és végzi el a szükséges logikai műveleteket.
// service/PostService.php
class PostService {
private $postRepository;
public function __construct(PostRepository $postRepository) {
$this->postRepository = $postRepository;
}
public function getPublishedPosts(): array {
$posts = $this->postRepository->findPublishedPosts();
// Itt lehetne még pl. cachelés, vagy egyéb logika
return $posts;
}
public function getPostForDisplay(int $id): ?array {
$post = $this->postRepository->findById($id);
if ($post && $post['status'] === 'published') {
return $post;
}
return null; // vagy dobhatnánk kivételt
}
}3. Prezentációs réteg (Controller)
Ez fogadja a HTTP kéréseket, meghívja a Service-t, és eldönti, mit jelenítsen meg. Szép és tiszta.
// controller/BlogController.php
class BlogController {
private $postService;
public function __construct(PostService $postService) {
$this->postService = $postService;
}
public function listPosts() {
$posts = $this->postService->getPublishedPosts();
// Itt betölthetjük a view-t és átadjuk neki az adatokat
require 'view/blog/list.php';
}
public function showPost($postId) {
$post = $this->postService->getPostForDisplay($postId);
if (!$post) {
http_response_code(404);
require 'view/errors/404.php';
return;
}
require 'view/blog/show.php';
}
}És a frontend ebben a képben?
A réteges architektúra nem csak backend dolog. A frontend kódodat is érdemes strukturálni, hogy ne legyen egy nagy kavalkád jQuery eseménykezelők és DOM manipulációk. Itt is létezhet egyfajta logikai réteg (pl. egy külön JavaScript modul, ami API hívásokat kezel) és a prezentációs réteg (ami a megjelenítésért felel).
// Egy egyszerű modul, ami a blogposztok API kommunikációját kezeli
// Ez a mi "frontend service" rétegünk lehet
var BlogApiClient = (function() {
function fetchPublishedPosts() {
return $.ajax({
url: '/api/posts',
method: 'GET',
dataType: 'json'
});
}
return {
fetchPublishedPosts: fetchPublishedPosts
};
})();
// A prezentációs logika egy másik helyen
$(document).ready(function() {
$('#load-posts-btn').on('click', function() {
BlogApiClient.fetchPublishedPosts()
.done(function(posts) {
// ... megjelenítjük a postokat a DOM-ban
var $container = $('#posts-container');
$container.empty();
$.each(posts, function(i, post) {
$container.append(
'<div class="card mb-3">' +
'<div class="card-body">' +
'<h5 class="card-title">' + post.title + '</h5>' +
'<p class="card-text">' + post.excerpt + '</p>' +
'</div></div>'
);
});
})
.fail(function() {
alert('Hiba a bejegyzések betöltésekor!');
});
});
});És a megjelenítés alapjait SCSS-szel is szépen tudjuk szabályozni:
// _blog.scss - a blog komponens stílusai
#posts-container {
.card {
border-left: 4px solid $primary;
&-title {
font-weight: 600;
color: $dark;
}
&-text {
color: $secondary;
font-size: 0.95rem;
}
}
}Gyakori buktatók, amiket kerüljünk el
1. A rétegek fanatikus túlzása: Nem kell minden egyes funkcióhoz külön interfész, abstract class és trait. Ha egy egyszerű DTO (Data Transfer Object) elég, ne csinálj belőle öröklődési fát. 2. A rétegek közötti szigorú függőség felforgatása: Kezdetben elég, ha a felsőbb rétegek (pl. Controller) ismerik az alattuk lévőket (pl. Service), de fordítva nem. Ne kezdd el a dependency injection containertől mindentől függővé tenni a kódot az első héten. 3. A „majd egyszer” szindróma: „Majd egyszer talán több adatforrásunk lesz, ezért most azonnal csináljunk egy Repository interfészt!” Ha nincs konkrét igény rá, halaszd a jövőre. A YAGNI (You Aren’t Gonna Need It) elv a barátod. 4. A frontend elhanyagolása: Az architektúra nem áll meg a szerver oldalon. A JavaScript kódod is könnyen áttekinthetetlen káoszban végzi, ha nem gondolod végig a felépítését.
Röviden összefoglalva
A réteges alkalmazásarchitektúra kialakításának titka a mértékletesség. Célja nem a bonyolultság, hanem a felelősségek szétválasztása, ami hosszú távon óriási időt takarít meg. Kezdd három egyszerű réteggel: adatelérés, üzleti logika, prezentáció. Építs tiszta határokat közöttük, de ne tervezz túl a jövőre. Hagyd, hogy az alkalmazás növekedésével együtt az architektúrád is érettséget szerezzen, de alapozz meg mindent olyan clean code alapelvekkel, amelyek lehetővé teszik a későbbi bővítést.
Ne feledd: a legjobb architektúra az, amelyik megengedi, hogy holnap is szívesen dolgozz a saját kódodon.