Я работаю над проектом, который генерирует API с возможностью выполнения операций CRUD на основе высокоуровневого описания ресурсов, которые пользователь хотел бы иметь в приложении. В процессе генерации кода необходимо выполнить несколько шагов (для некоторого контекста, я генерирую код на Python из Python с помощью Jinja2):
Почти все операции зависят от ввода данных пользователем, но никак не связаны напрямую, поэтому могут выполняться даже параллельно.
Исследуя способы правильной обработки последовательных операций, я нашел паттерн "Цепочка ответственности", но он не совсем применим в моей ситуации, поскольку я должен выполнить все шаги независимо от ввода пользователя (если только это правильный ввод).
Я также думал о чем-то вроде паттерна Strategy для каждого процесса генерации, но мне трудно решить, подходит ли какой-либо из этих паттернов для моей ситуации.
В настоящее время я структурировал свой код следующим образом:
r = RelationshipHandler(Input(**res).resources)
r.execute()
resources = [resource.dict() for resource in r.resources]
generate_connection()
generate_db_create_code(resources)
generate_docker_compose()
generate_pydantic_models(resources)
generate_sqlalchemy_classes(resources)
generate_model_code(resources)
generate_fastapi_code(resources)
Все эти операции выполняются оркестрантом/хореографом - что, по сути, означает, что я использую, по крайней мере, чрезвычайно простую версию паттерна Choreography (более распространенного в контексте микросервисов).
Мой вопрос заключается в следующем: как подобные ситуации решаются в целом? Какие паттерны или лучшие практики позволят сделать код более чистым/читабельным/логичным и более легким в сопровождении?
Я понимаю, что может существовать множество способов обработки ситуаций, подобных описанной, я просто пытаюсь найти другие, более простые способы решения той же проблемы.
Ключевым моментом, однако, является то, чтобы сделать вход и выход каждого из шагов явным, вместо того, чтобы использовать "глобальное состояние" для передачи данных.
Например:
conn=generate_connection()
my_code = generate_db_create_code(conn,resources)
generate_docker_compose(my_code)
(приведенные выше входные/выходные данные могут быть неверными, но, думаю, вы поняли идею).
Цели, стоящие за этим, следующие:
должно стать более понятным, что делает каждый шаг, и
каждый шаг должен быть изолирован с возможностью модульного тестирования, и
становится менее подверженным ошибкам, когда вам приходится добавлять или изменять порядок некоторых шагов.
Это типичные цели, которые вы должны иметь в виду при проектировании, а не какие-то шаблоны, о которых вы слышали.