Clean Architecture & Django
Уже почти полгода я курирую разработку проекта и во многом диктую чистую архитектуру в разработке. К чему это привело:
- Чистые сценарии предметки которые не зависят от Django от слова совсем
- Гибкость в создании архитектурных элементов
- Свой ORM над Django-ORM (Это как бы шутка, но на самом деле правда)
Сегодня пообщались с коллегами из другой команды: они тоже на Django но
используют по-максимому Django-ORM и готовые библиотеки. Пока что размеры не
соизмеримы, поэтому сложно сравнивать решения, однако у меня кое-что зацепило.
На примере state-machine: была использована django-fsm которая диктует свои
правила и ограничения в реализации состояний. Често говоря, по библиотеке
сложно понять как с ней работать. Да и выглядит это…не гибко, не нативно к
проекту. И так будет с каждым решением. И ведь написание своего велосипеда тоже
будет выглядеть костылем, потому что это не Django.
В нашем решении, есть набор сущностей предметной области и вереница: UseCase,
Presenter, Storage. UseCase (он же Interactor) представляет собой основные
функции бизнес логики. Их удобно инициализировать и запускать и чувствуется
необходимость в создании структуры UseCaseOutputPort - сейчас мы возращаем
из сценариев словари, которые на самом деле возращает Presenter который
вызывается в конце функции execute сценария. Это был сильный косяк.
Во-первых это дополнительный Dependency Injection в UseCase, а во вторых он
не нужен по простой причине: в рамках Django, роль Presenter может выполнять
view. Это выглядит правильным, потому что зачем выносить спецефичный для
текущей реализации (т.е. Django) в какие-то отдельные классы и оставлять
view только логику формирования входных параметров для UseCase.
Конечно мы начали достаточно скоро реагировать на изменения бизнеса, что
привело к созданию концепта Workflow - та же state-machine. Это получилось не
в самом лучшем виде т.к. делалось на скорую руку, но в целом это получилось
расширяемым решением: для разных команд, у которых слишком разные рабочие
процессы, мы смогли писать отдельные классы с их кастомной логикой. Причем мы
можем себе позволить отдать реализацию таких классов разрабочикам команд,
которые пользуются нашим софтом, потому что API прост и использует лишь пару
классов (структур пока-что) из предметной области. И все это API не зависит
от Django или чего-то еще. Оно краткое.
Конечно лошка дегтя есть: сейчас не очень понятно когда и как вызываются
функции Workflow. Более того, остается гибкость в выборе реализации
state-machine: можно взять и стороннее решение и по своему написать. Причем
есть Workflow и без набора состояний: это такие Workflow в которых из
каждого состояния можно перейти в каждое, т.е. Workflow не управляет этим. Все
что он делает это…ничего. NoopWorkflow - реализация по-умолчанию. Однако с
точки зрения чистой архитектуры, в целом, все выглядит достаточно приятно.
Выработались такие патерны:
-
Создавайте связку
StorageиPresenter(View) с привязкой к конкретномуUseCase- это позволит больше сконцентрироваться на сценарии и проводить при необходимости жуткие оптимизации ценой небольшого дублирования. -
Storage- это просто набор операций который необходим конкретному сценарию. КодStorageдолжен быть максимально простым. Особое внимание нужно обращать на вызовы других функций и условные операторы: чаще всего это признак того, что часть бизнес-логики лежит вStorage -
Сразу же стоит озаботиться написанием сериализаторов из моделей Django в модели предметной области. Можно конечно считерить и обойтись без этого, отдавая сами модели, но стоит ограничить себя в обращениях к ORM-API который предоставляет
Django-ORMвнеStorageпотому что тогда привязанность кDjango-ORMостанется и само приложение будет уже Django’вским.
У меня остался открытым самый жывотрепещущий вопрос: что делать с этими чертовыми ReactJS и Redux?! Пока что они живут своей жизнью, но ощущается что это не правильно. Им не хватает “чистоты”.