Как спроектировать пользовательский интерфейс для отображения процесса выполнения длительной бизнес-операции, используя принципы Clean Architecture?

Здравствуйте! <br/> <br/> Я стремлюсь разработать программу с правильной архитектурой. <br/> Я ознакомился с концепцией Чистой архитектуры: <br/> <a href="https://habr.com/ru/articles/499078/" rel="nofollow">https://habr.com/ru/articles/499078/</a> <br/> <br/> Из моего понимания, в рамках Чистой архитектуры выходные данные бизнес-операции (Use Case Interactor) возвращаются вызывающему слою через OutputPort только по окончании выполнения операции. <br/> Однако, мне интересно, как можно изменить пользовательский интерфейс в процессе выполнения длительной бизнес-операции? <br/> <br/> Например, если бизнес-операция заключается в обработке вложенных файлов и папок, и эта обработка занимает значительное время, мне необходимо, чтобы в интерфейсе отображалась таблица с колонками: "Имя папки", "Обрабатываемый файл" и "Результат обработки". При старте обработки новой папки или подпапки в таблице должна появляться новая строка, где в первой колонке отображается имя папки. По мере обработки файлов во второй колонке должно обновляться сообщение о том, что выполняется обработка файла. В итоге, после завершения обработки всех файлов, в колонке "Результат" должна появляться информация об итогах обработки папки. <br/> <br/> Это может казаться несложной задачей, но каким образом это реализовать, следуя принципам Чистой архитектуры? Что должен делать Контроллер, как именно следует вызывать UseCaseInteractor? Какие действия должна выполнять бизнес-операция, чтобы задействовать все компоненты OutputPort, Presenter и т.д., и в конечном итоге обеспечить отображение всех этих обновлений в интерфейсе?
  • 18 сентября 2024 г. 6:43
Ответы на вопрос 2
Проектирование пользовательского интерфейса для отображения длительных бизнес-операций в рамках Чистой архитектуры требует особого подхода, чтобы следовать принципам разделения ответственности и поддерживать возможность тестирования и модификации. Ниже приведен подход, который может помочь вам реализовать такое решение:

### Структура архитектуры

1. **Слой ввода (Presentation Layer)**:
   - **Контроллер (Controller)**: отвечает за обработку пользовательского ввода и взаимодействие с представлением. Он будет получать события из представления (например, нажатие кнопки "Начать обработку") и инициировать вызов соответствующего Use Case.
   - **Презентер (Presenter)**: принимает данные от Use Case (через OutputPort) и преобразует их в формат, понятный для отображения в UI.

2. **Слой бизнес-логики (Domain Layer)**:
   - **Use Case (Interactor)**: выполняет бизнес-логику и управляет состоянием операции. Он будет обращаться к описание проблемы и передавать обновления в Презентер.
   - **OutputPort**: интерфейс, через который Use Case сообщает о состоянии выполнения и изменениях данных.

3. **Слой данных (Data Layer)**:
   - Отвечает за взаимодействие с внешними системами/данными (например, записи в базу данных или обращения к файловой системе).

### Процесс выполнения длительной операции

1. **Инициация операции**:
   - Контроллер обрабатывает событие начала обработки (например, нажатие кнопки) и вызывает метод в Use Case Interactor для начала длительной операции.

2. **Взаимодействие Use Case и OutputPort**:
   - Внутри Use Case по мере выполнения длительной операции (например, обработки файлов и папок) будут происходить разные этапы, и в каждом из них Use Case будет вызывать методы OutputPort для передачи состояния (например, начало обработки новой папки, текущий обрабатываемый файл, результаты).

3. **Обновление UI через Презентер**:
   - Презентер будет получать данные от OutputPort, преобразовывать их в структуру данных для отображения в UI и обновлять представление в пользовательском интерфейсе.

4. **Побочные эффекты и события**:
   - Чтобы обеспечить «реактивность» интерфейса, вы можете использовать паттерн публикации/подписки. Презентер будет подписываться на изменения состояния и обновлять UI по мере поступления данных от Use Case.

### Пример реализации

```python
# OutputPort интерфейс
class OutputPort:
    def on_folder_started(self, folder_name: str):
        pass

    def on_file_processing(self, file_name: str):
        pass

    def on_folder_processed(self, folder_name: str, result: str):
        pass

# Use Case
class FileProcessingUseCase:
    def __init__(self, output_port: OutputPort):
        self.output_port = output_port

    def process_files(self, folder_path: str):
        # Обрабатываем папку
        self.output_port.on_folder_started(folder_path)
        
        # Имитация обработки файлов
        for file in os.listdir(folder_path):
            self.output_port.on_file_processing(file)
            time.sleep(1)  # Имитация длительной обработки
            result = "Успех"  # Результат обработки файла
        self.output_port.on_folder_processed(folder_path, result)

# Презентер
class FileProcessingPresenter(OutputPort):
    def update_ui(self):
        # Логика обновления UI
        pass

    def on_folder_started(self, folder_name: str):
        # Добавляем строку с именем папки в таблицу
        self.update_ui()

    def on_file_processing(self, file_name: str):
        # Обновляем состояние обработки файла
        self.update_ui()

    def on_folder_processed(self, folder_name: str, result: str):
        # Записываем результат в таблицу
        self.update_ui()

# Контроллер
class FileProcessingController:
    def __init__(self, use_case: FileProcessingUseCase):
        self.use_case = use_case

    def start_processing(self, folder_path: str):
        self.use_case.process_files(folder_path)

# Пример использования
presenter = FileProcessingPresenter()
use_case = FileProcessingUseCase(presenter)
controller = FileProcessingController(use_case)

# Запуск обработки
controller.start_processing("/path/to/folder")
```

### Заключение
Такой подход позволяет вам разбить логику на четкие компоненты, следовать принципам Чистой архитектуры и гарантировать, что пользовательский интерфейс будет обновляться
Мартин придумал сову, а вы теперь на свой глобус ее натягиваете. Не знаю что за OutputPort, видимо в терминологии из статьи это View Model. Примерно так это может выглядеть схематично
Похожие вопросы