type: article
keywords:
- 3-Schichten-Modell
- 3-Tier Architektur
- Präsentation Business Daten
- DTO
canonicalURL: https://www.irc-coding.de/3-schichten-modell-3-tier-architektur
Архитектура ПО: трехслойная модель (3-Tier)
Этот материал посвящен трехслойной архитектуре (3-Tier) с примерами, ключевыми моментами и практическими рекомендациями.
Суть в двух словах
Трехслойная модель разделяет приложение на три логических уровня: представление, бизнес-логика и доступ к данным. Это обеспечивает четкую ответственность, упрощает поддержку и тестирование.
Техническое описание
Три уровня архитектуры:
- Presentation (UI)
- Business Logic (Services/Use Cases)
- Persistence (Repository/ORM/DB)
Данные обычно движутся сверху вниз. Каждый слой знает только о слое, который находится ниже. Это создает слабую связанность между компонентами.
Ключевые моменты для обучения
- Разделение представления, логики и доступа к данным
- Четкое распределение ответственности
- Лучшая тестируемость и поддерживаемость
- Стандарт для Java, .NET и веб-приложений
- Возможность замены отдельных компонентов
- Слои служат границами безопасности
- Модульность снижает затраты на изменения
- Архитектура должна быть задокументирована
Основные компоненты
- Слой представления
- Слой логики
- Слой доступа к данным
- Интерфейсы между слоями
- Логирование и обработка ошибок
- Unit-тесты в бизнес-слое
- DTO
- Слой безопасности
- Хранилище (SQL/NoSQL)
Практический пример: управление задачами на Python
Что показывает этот пример? Простая система управления задачами на Python, построенная строго по трехслойной модели. Данные проходят от представления (CLI) через бизнес-слой (валидация, правила) к слою персистентности (SQLite-репозиторий).
Почему это хороший пример для обучения? Каждый слой тестируется отдельно. Бизнес-слой не требует базы данных, представление не требует SQLite — они взаимодействуют только через чистые вызовы методов.
import sqlite3
from dataclasses import dataclass
from typing import List, Optional
# === Слой 3: Persistence (доступ к данным) ===
@dataclass
class TaskDTO:
id: int; title: str; description: str; status: str
class TaskRepository:
def __init__(self, db_path="tasks.db"):
self.db_path = db_path
with sqlite3.connect(db_path) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL, description TEXT,
status TEXT DEFAULT 'open')""")
def create(self, task: TaskDTO) -> TaskDTO:
with sqlite3.connect(self.db_path) as conn:
c = conn.execute("INSERT INTO tasks (title,description,status) VALUES (?,?,?)",
(task.title, task.description, task.status))
task.id = c.lastrowid
return task
def get_all(self) -> List[TaskDTO]:
with sqlite3.connect(self.db_path) as conn:
rows = conn.execute("SELECT * FROM tasks").fetchall()
return [TaskDTO(*r) for r in rows]
def get_by_id(self, tid: int) -> Optional[TaskDTO]:
with sqlite3.connect(self.db_path) as conn:
r = conn.execute("SELECT * FROM tasks WHERE id=?", (tid,)).fetchone()
return TaskDTO(*r) if r else None
def update(self, task: TaskDTO) -> TaskDTO:
with sqlite3.connect(self.db_path) as conn:
conn.execute("UPDATE tasks SET title=?,description=?,status=? WHERE id=?",
(task.title, task.description, task.status, task.id))
return task
def delete(self, tid: int) -> bool:
with sqlite3.connect(self.db_path) as conn:
return conn.execute("DELETE FROM tasks WHERE id=?", (tid,)).rowcount > 0
# === Слой 2: Business Logic (бизнес-логика) ===
class TaskService:
def __init__(self, repo: TaskRepository):
self._repo = repo
def create_task(self, title: str, description="") -> TaskDTO:
if not title or len(title.strip()) == 0:
raise ValueError("Название не может быть пустым")
if len(title) > 100:
raise ValueError("Название не более 100 символов")
return self._repo.create(TaskDTO(0, title.strip(), description, "open"))
def complete_task(self, task_id: int) -> TaskDTO:
task = self._repo.get_by_id(task_id)
if not task:
raise ValueError(f"Задача {task_id} не найдена")
task.status = "done"
return self._repo.update(task)
def list_open_tasks(self) -> List[TaskDTO]:
return [t for t in self._repo.get_all() if t.status != "done"]
# === Слой 1: Presentation (CLI) ===
class TaskCLI:
def __init__(self, service: TaskService):
self._service = service
def run(self):
while True:
print("\n1=Новая задача 2=Открытые 3=Завершить 4=Выход")
choice = input("Выбор: ")
if choice == "1":
title = input("Название: ")
desc = input("Описание: ")
try:
task = self._service.create_task(title, desc)
print(f"Задача {task.id} создана")
except ValueError as e:
print(f"Ошибка: {e}")
elif choice == "2":
for t in self._service.list_open_tasks():
print(f"[{t.id}] {t.title} ({t.status})")
elif choice == "3":
tid = int(input("ID задачи: "))
try:
self._service.complete_task(tid)
print("Завершено")
except ValueError as e:
print(f"Ошибка: {e}")
elif choice == "4":
break
if __name__ == "__main__":
repo = TaskRepository()
service = TaskService(repo)
cli = TaskCLI(service)
cli.run()
Преимущества и недостатки
Преимущества
- Структурированное, поддерживаемое приложение
- Легкая замена отдельных компонентов
- Хорошая тестируемость
Недостатки
- Больше кода на старте
- Избыточность в очень маленьких проектах
Типовые вопросы экзамена (с кратким ответом)
- Что описывает трехслойная модель? Представление, логика, доступ к данным.
- Какой слой выполняет валидацию? Бизнес-логика.
- Что входит в слой доступа к данным? DB-операции, SQL/ORM, репозитории.
FAQ — часто задаваемые вопросы о трехслойной модели
В: Какая разница между трехслойной моделью и 3-Tier архитектурой? О: Трехслойная модель описывает логическое разделение (представление, бизнес, персистентность) — все слои могут работать на одном сервере. 3-Tier архитектура описывает физическое разделение на три машины: клиент, сервер приложений и сервер БД. На экзаменах оба термина часто используют как синонимы.
В: Почему нельзя обращаться из UI напрямую к БД? О: Прямые обращения создают сильную связанность. Изменения схемы БД требуют правок во всех местах UI. Бизнес-правила (валидация) нельзя централизовано контролировать. Сложнее защищаться от SQL-injection. Бизнес-слой выступает как центральная точка контроля и защиты.
В: Где точные границы между слоями? О: Представление знает только бизнес-слой и передает сырые данные. Бизнес-слой валидирует и передает DTO в слой персистентности. Персистентность работает только с SQL/ORM. Если в репозитории есть методы вроде “валидировать” или “проверить”, граница нарушена.
В: Что такое DTO и зачем оно нужно?
О: DTO (Data Transfer Object) содержит только данные, без логики. В примере Python это TaskDTO. DTO разделяют слои, так как каждый слой знает только формат DTO, но не внутренние структуры других слоев.
В: Когда трехслойная модель не нужна? О: В очень маленьких скриптах (несколько сотен строк) архитектура избыточна. Как только нужны unit-тесты или БД должна быть заменяемой, разделение окупается. Правило: скрипт одного человека = плоская структура, командный проект или приложение = 3 слоя.
Собственный опыт
Для проектов на экзаменах эта модель идеальна, потому что легко рисуется и объясняется. Важно: никаких SQL в контроллере и доступа к БД из UI.
Стратегия обучения
- Нарисуйте модель для какой-то системы (магазин, блог).
- Реализуйте CRUD-приложение строго по слоям.
- Объясните разделение в документации проекта.
- Реализуйте разделение через пакеты и пространства имен.


