Ответ
Entity (Сущность) — это ядро бизнес-логики приложения, представляющее фундаментальные концепции предметной области. В моих Flutter-проектах, построенных по принципам Clean Architecture, Entity находятся в самом центре, не завися от фреймворков, UI или внешних сервисов.
Пример реализации Entity для приложения управления задачами:
// Value Object для идентификатора задачи
class TaskId {
final String value;
TaskId(this.value) {
if (value.isEmpty) {
throw ArgumentError('TaskId cannot be empty');
}
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TaskId &&
runtimeType == other.runtimeType &&
value == other.value;
@override
int get hashCode => value.hashCode;
}
// Value Object для статуса задачи
enum TaskStatus { todo, inProgress, done }
// Основная Entity - задача
class Task {
final TaskId id;
final String title;
final String description;
final TaskStatus status;
final DateTime createdAt;
final DateTime? completedAt;
final List<String> tags;
Task({
required this.id,
required this.title,
required this.description,
required this.status,
required this.createdAt,
this.completedAt,
this.tags = const [],
}) {
// Бизнес-правила валидации
if (title.isEmpty) {
throw ArgumentError('Title cannot be empty');
}
if (completedAt != null && status != TaskStatus.done) {
throw ArgumentError('Only completed tasks can have completion date');
}
}
// Бизнес-методы (логика предметной области)
Task markAsInProgress() {
return Task(
id: id,
title: title,
description: description,
status: TaskStatus.inProgress,
createdAt: createdAt,
completedAt: completedAt,
tags: tags,
);
}
Task complete() {
return Task(
id: id,
title: title,
description: description,
status: TaskStatus.done,
createdAt: createdAt,
completedAt: DateTime.now(),
tags: tags,
);
}
bool get isOverdue {
final deadline = createdAt.add(const Duration(days: 7));
return status != TaskStatus.done && DateTime.now().isAfter(deadline);
}
}
Принципы работы с Entity в моих проектах:
- Независимость от фреймворка — Entity не импортируют flutter/dart:ui
- Богатая модель — содержат бизнес-правила и методы
- Иммутабельность — все поля
final, методы возвращают новые экземпляры - Value Objects — использование специализированных объектов для примитивов
- Легкая тестируемость — можно тестировать без Flutter-зависимостей
Преобразование между слоями:
// Из DTO в Entity
Task fromDto(TaskDto dto) {
return Task(
id: TaskId(dto.id),
title: dto.title,
description: dto.description,
status: TaskStatus.values.firstWhere((e) => e.name == dto.status),
createdAt: DateTime.parse(dto.createdAt),
completedAt: dto.completedAt != null ? DateTime.parse(dto.completedAt!) : null,
tags: dto.tags,
);
} Ответ 18+ 🔞
А, ну это про сущности, ёпта! Слушай, я тебе сейчас так объясню, что ты сам от себя охуеешь. Entity — это типа священный грааль твоего приложения, его фундамент, на котором всё держится. Представь, что это скелет бизнес-логики, который не должен ни от кого зависеть. Ни от Flutter, ни от UI, который каждые полгода меняется, ни от этих твоих внешних API, которые могут накрыться медным тазом в любой момент.
Вот смотри, как я в своих проектах это делаю. Беру, например, приложение для задач. Ну, там тудушки всякие. Так вот, первое правило — никакого Flutter внутри! Серьёзно, если ты импортируешь flutter/dart:ui в свою сущность, ты уже проебался. Это должно быть чистое ядро, которое можно взять и перенести хоть на сервер, хоть в консольное приложение — и оно будет работать.
Вот пример, как я это обожаю делать:
// Value Object для ID задачи. Не просто строка, а обёртка, чтоб не путать с другими ID.
class TaskId {
final String value;
TaskId(this.value) {
if (value.isEmpty) {
throw ArgumentError('TaskId cannot be empty'); // Защита от дурака
}
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TaskId &&
runtimeType == other.runtimeType &&
value == other.value;
@override
int get hashCode => value.hashCode;
}
// Статус задачи — тоже не просто строка, а enum. Чётко и понятно.
enum TaskStatus { todo, inProgress, done }
// А вот и главная сука — сама Task. Entity, мать её.
class Task {
final TaskId id;
final String title;
final String description;
final TaskStatus status;
final DateTime createdAt;
final DateTime? completedAt;
final List<String> tags;
Task({
required this.id,
required this.title,
required this.description,
required this.status,
required this.createdAt,
this.completedAt,
this.tags = const [],
}) {
// А тут бизнес-правила, ёбать копать! Валидация на месте.
if (title.isEmpty) {
throw ArgumentError('Title cannot be empty'); // Без названия — не задача, а хуйня.
}
// Вот это важно! Нельзя иметь дату завершения у незавершённой задачи. Логика!
if (completedAt != null && status != TaskStatus.done) {
throw ArgumentError('Only completed tasks can have completion date');
}
}
// Бизнес-методы! Это не просто геттеры-сеттеры. Это реальные действия предметной области.
Task markAsInProgress() {
// Иммутабельность, чувак! Не меняем старый объект, а создаём новый.
return Task(
id: id,
title: title,
description: description,
status: TaskStatus.inProgress,
createdAt: createdAt,
completedAt: completedAt,
tags: tags,
);
}
Task complete() {
return Task(
id: id,
title: title,
description: description,
status: TaskStatus.done,
createdAt: createdAt,
completedAt: DateTime.now(), // Запоминаем, когда завершили
tags: tags,
);
}
// Вычисляемое свойство — просрочена ли задача?
bool get isOverdue {
final deadline = createdAt.add(const Duration(days: 7)); // Допустим, неделя на выполнение
return status != TaskStatus.done && DateTime.now().isAfter(deadline);
}
}
Видишь, в чём фишка? Богатая модель. Сущность не просто мешок с данными, она знает, что с ней можно делать. Она сама себя валидирует. Её можно протестировать в полной изоляции, без всяких flutter test, просто обычным dart test. Доверия к такой штуке — овердохуища.
А когда нужно данные из внешнего мира (от API, например) превратить в эту самую Entity, делаем чёткий маппинг:
// Из DTO (Data Transfer Object, этакой грязной посылки с сервера) в нашу чистую Entity.
Task fromDto(TaskDto dto) {
return Task(
id: TaskId(dto.id),
title: dto.title,
description: dto.description,
status: TaskStatus.values.firstWhere((e) => e.name == dto.status), // Преобразуем строку в enum
createdAt: DateTime.parse(dto.createdAt),
completedAt: dto.completedAt != null ? DateTime.parse(dto.completedAt!) : null,
tags: dto.tags,
);
}
Короче, суть в чём: Entity — это святое. Это твоя предметная область в чистом виде. Всё остальное — UI, базы данных, апишки — это просто шелуха вокруг этого ядра. Сделаешь так — и жить станет проще, а код перестанет быть пиздопроебибной кашей, где всё зависит ото всего.