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?! Пока что они живут своей жизнью, но ощущается что это не правильно. Им не хватает “чистоты”.