131 lines
4.8 KiB
Markdown
131 lines
4.8 KiB
Markdown
### **Пример приложения**
|
||
|
||
В данном примере веб-интерфейс отсутствует, функционал можно протестить юнит-тестами.
|
||
|
||
Тесты в папке ``./tests``
|
||
|
||
### **Запуск тестов:**
|
||
|
||
Все тесты
|
||
|
||
``php artisan test``
|
||
|
||
Только юнит-тесты
|
||
|
||
``php artisan test --testsuite=Unit``
|
||
|
||
Конкретный тест
|
||
|
||
``php artisan test tests/Unit/UserTest.php``
|
||
|
||
С покрытием кода
|
||
|
||
``php artisan test --coverage``
|
||
|
||
### **Модули приложения**
|
||
|
||
Код разделен на модули. Позволит проще распараллелить задачи между несколькими разработчиками. Позволит переиспользовать готовые модули в разных проектах.
|
||
|
||
Модули в папке ``./modules`` [Подробней](./modules/Readme.md)
|
||
|
||
|
||
|
||
|
||
|
||
### **В результате получаем**
|
||
|
||
#### **① Контроль над SQL-запросами**
|
||
**Проблема Eloquent:**
|
||
- Генерирует сложные SQL, которые трудно оптимизировать
|
||
- N+1 проблема (ленивая загрузка отношений)
|
||
|
||
**Решение:**
|
||
```php
|
||
public function getList(int $limit, int $offset, array $dsl = []): array
|
||
{
|
||
$data = $this->tasksQuery->select($dsl)->limit($limit, $offset)->all();
|
||
// ...
|
||
}
|
||
```
|
||
**Преимущества:**
|
||
- Явный контроль над запросами через `TasksQuery`
|
||
- Четкое разделение на простые запросы (`select()`, `limit()`)
|
||
- Нет "магии", которую трудно дебажить
|
||
|
||
---
|
||
|
||
#### **② Производительность**
|
||
**Проблема Eloquent:**
|
||
- Гибертность моделей (каждая сущность — объект с оверхедом)
|
||
- Проблемы с большими выборками
|
||
|
||
**Решение:**
|
||
```php
|
||
private function receiveAdditionalData(array &$data, array $dsl): void
|
||
{
|
||
if (in_array('options', $dsl)) {
|
||
$tasksIds = $this->pluck('id', $data);
|
||
$otherData = $this->otherQuery->getForTasks($tasksIds); // Один запрос для всех задач
|
||
// ...
|
||
}
|
||
}
|
||
```
|
||
**Преимущества:**
|
||
- Работа с массивами вместо объектов (меньше потребление памяти)
|
||
- Явная загрузка связанных данных за 1 запрос (не N+1)
|
||
|
||
---
|
||
|
||
#### **③ Тестируемость**
|
||
**Проблема Eloquent:**
|
||
- Трудно мокировать (глобальные scope, трейты)
|
||
- Зависимость от статики (`DB::shouldReceive`)
|
||
|
||
**Решение:**
|
||
```php
|
||
public function setTasksQuery(TasksQuery $query): void {
|
||
$this->tasksQuery = $query; // Легко подменить mock-объектом
|
||
}
|
||
```
|
||
**Преимущества:**
|
||
- Чистые зависимости (интерфейсы или конкретные классы)
|
||
- Проще писать unit-тесты
|
||
|
||
---
|
||
|
||
#### **④ Гибкость архитектуры**
|
||
**Проблема Eloquent:**
|
||
- Жесткая привязка к ActiveRecord
|
||
- Сложно разделить на слои (логика в моделях)
|
||
|
||
**Ваше решение:**
|
||
```php
|
||
class TasksStorage {
|
||
// Слой хранилища
|
||
private TasksQuery $tasksQuery; // Отдельный класс для запросов
|
||
private OtherQuery $otherQuery; // Отдельный класс для связей
|
||
}
|
||
```
|
||
**Преимущества:**
|
||
- Четкое разделение:
|
||
- `TasksQuery` — построение SQL
|
||
- `TasksStorage` — бизнес-логика доступа к данным
|
||
- DTO — передача данных между слоями
|
||
|
||
---
|
||
|
||
### **3. Наглядное сравнение**
|
||
|
||
| Критерий | Eloquent | Данный подход |
|
||
|-------------------|-------------------|-------------------|
|
||
| Производительность | Низкая (N+1, оверхед) | Высокая (оптимизированные запросы) |
|
||
| Тестируемость | Сложная | Простая (DI) |
|
||
| Контроль SQL | Ограниченный | Полный |
|
||
| Поддержка | Легкая для простых проектов | Лучше для сложных систем |
|
||
|
||
---
|
||
|
||
**Данный подход — это "прозрачность и контроль", где:**
|
||
- Каждая операция предсказуема
|
||
- Легко оптимизировать под нагрузку
|
||
- Архитектура готова к масштабированию |