Что такое DDD (Domain-Driven Design)?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое DDD (Domain-Driven Design)
Domain-Driven Design (DDD) — это методология проектирования программного обеспечения, которая фокусируется на бизнес-домене (области деятельности) как центре разработки. DDD помогает создавать системы, которые точно отражают реальные бизнес-процессы.
Основная идея
Вместо того, чтобы начинать с технологий ("давайте используем React и Redux"), DDD говорит:
Начни с понимания бизнеса!
Бизнес-требования → Язык (Ubiquitous Language) → Архитектура → Технологии
Ключевые концепции DDD
1. Ubiquitous Language (Единый язык)
Технические специалисты, продакт-менеджеры и бизнес должны использовать один словарь:
// Плохо: используем технические термины
const updateUserData = (user) => {...}; // Что такое "data"?
// Хорошо: используем язык бизнеса
const enrollUserInCourse = (user, course) => {...}; // Ясно: пользователь записывается на курс
const submitQuestionAnswer = (user, question, answer) => {...}; // Понятно сразу
Такой язык называется Ubiquitous Language — он общий для всей команды.
2. Domain (Домен)
Домен — это область деятельности компании. Например:
- Для эдтеха: обучение, вопросы, экзамены, студенты
- Для e-commerce: товары, заказы, доставка, платежи
- Для SaaS: подписки, аккаунты, биллинг
3. Bounded Context (Ограниченный контекст)
Большие системы делятся на подмножества (контексты), каждый со своим языком и моделями:
Онлайн-школа
├── Контекст "Обучение" (учебные материалы, прогресс, оценки)
├── Контекст "Платежи" (транзакции, подписки, счета)
├── Контекст "Аутентификация" (логин, сессии, права доступа)
└── Контекст "Аналитика" (статистика, отчеты)
В каждом контексте могут быть разные определения одного сущности. Например:
- В контексте "Обучение" "User" имеет
progressиanswers - В контексте "Платежи" "User" имеет
subscriptionиinvoices
4. Entity (Сущность)
Сущность — это объект, который имеет уникальный идентификатор и жизненный цикл:
class User {
constructor(id, name, email) {
this.id = id; // Уникальный идентификатор
this.name = name;
this.email = email;
}
// Сущность может менять свое состояние
enrollCourse(course) {
if (this.canEnroll()) {
this.enrolledCourses.push(course);
}
}
}
5. Value Object (Объект-значение)
Value Object — это объект, который определяется своими значениями, а не идентификатором:
// Value Object: Email
class Email {
constructor(value) {
if (!this.isValid(value)) {
throw new Error("Invalid email");
}
this.value = value;
}
isValid(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
equals(other) {
return this.value === other.value; // Сравнение по значению
}
}
const email1 = new Email("user@example.com");
const email2 = new Email("user@example.com");
console.log(email1.equals(email2)); // true — одинаковые значения
6. Aggregate (Агрегат)
Агрегат — это группа связанных сущностей и value objects, которые работают как одно целое:
class Course {
constructor(id, title) {
this.id = id;
this.title = title;
this.lessons = []; // Сущность
this.students = []; // Сущность
}
addLesson(lesson) {
this.lessons.push(lesson);
}
enrollStudent(student) {
this.students.push(student);
}
}
// Course агрегирует Lesson и Student
const course = new Course(1, "JavaScript");
course.addLesson(new Lesson(1, "Основы"));
course.enrollStudent(student);
7. Repository (Хранилище)
Repository — это абстракция для доступа к данным:
class UserRepository {
async findById(id) {
// Может быть из БД, API, кэша...
return db.query("SELECT * FROM users WHERE id = ?", [id]);
}
async save(user) {
return db.query("INSERT INTO users VALUES (...)");
}
}
// Использование
const userRepo = new UserRepository();
const user = await userRepo.findById(123);
8. Service (Сервис)
Service — это операция, которая не принадлежит сущности, но отражает бизнес-логику:
class EnrollmentService {
async enrollUserInCourse(userId, courseId) {
const user = await this.userRepository.findById(userId);
const course = await this.courseRepository.findById(courseId);
if (!user.canAfford(course.price)) {
throw new Error("Insufficient funds");
}
user.enrollCourse(course);
await this.userRepository.save(user);
}
}
DDD в фронтенде (React)
DDD не только для бэкенда. Фронтенд может тоже организовать по доменам:
src/
├── domains/
│ ├── users/
│ │ ├── types.ts (User, UserProfile)
│ │ ├── services.ts (UserService)
│ │ └── hooks.ts (useUser, useUserProfile)
│ ├── courses/
│ │ ├── types.ts (Course, Lesson)
│ │ ├── services.ts (CourseService)
│ │ └── hooks.ts (useCourse, useLessons)
│ └── enrollment/
│ ├── types.ts (Enrollment)
│ ├── services.ts (EnrollmentService)
│ └── hooks.ts (useEnrollment)
├── components/ (UI слой)
└── pages/ (маршруты)
Пример: Онлайн-школа (DDD подход)
// Domain слой
type UserId = string & {readonly __brand: "UserId"};
type CourseId = string & {readonly __brand: "CourseId"};
interface User {
id: UserId;
name: string;
email: string;
enrolledCourses: CourseId[];
}
interface Course {
id: CourseId;
title: string;
price: number;
lessons: Lesson[];
}
interface Enrollment {
userId: UserId;
courseId: CourseId;
enrolledAt: Date;
progress: number;
}
// Application слой
class EnrollmentService {
async enrollUserInCourse(
userId: UserId,
courseId: CourseId
): Promise<Enrollment> {
const user = await this.userRepo.findById(userId);
const course = await this.courseRepo.findById(courseId);
if (user.enrolledCourses.includes(courseId)) {
throw new Error("Already enrolled");
}
const enrollment = {
userId,
courseId,
enrolledAt: new Date(),
progress: 0
};
return this.enrollmentRepo.save(enrollment);
}
}
// Presentation слой (React)
function CourseEnrollButton({ courseId, userId }: Props) {
const [isLoading, setIsLoading] = useState(false);
const enrollmentService = useEnrollmentService();
const handleEnroll = async () => {
setIsLoading(true);
try {
await enrollmentService.enrollUserInCourse(
userId as UserId,
courseId as CourseId
);
showSuccess("Enrolled successfully!");
} catch (error) {
showError(error.message);
} finally {
setIsLoading(false);
}
};
return <button onClick={handleEnroll}>Enroll</button>;
}
Преимущества DDD
- Понимание бизнеса — команда четко понимает, что и зачем делает
- Масштабируемость — легче разделить работу между разработчиками
- Гибкость — изменения в бизнесе отражаются в коде
- Качество — меньше ошибок благодаря четкой структуре
- Коммуникация — разработчики и бизнес говорят на одном языке
Заключение
DDD — это не просто архитектурный паттерн, а философия разработки, которая ставит бизнес-домен в центр всего. Это помогает создавать системы, которые действительно решают реальные проблемы.