Как стать автором
Обновить

Организация кода это важно и легко на основе Layer Architecture

Уровень сложностиПростой
Время на прочтение6 мин
Количество просмотров6.4K
Всего голосов 6: ↑4 и ↓2+2
Комментарии7

Комментарии 7

Да про слоистую архитектуру, мне кажется, во-первых, все знают уже со времен, когда кто-то догадался что можно на диск не байтики и секторы писать, а сделать файловую систему, чтоб по именам к областям хранения данных обращаться. Реально самая древняя архитектура наверное. А во-вторых, это самый очевидный подход, первая мысль, которая возникает в голове начинающего разработчика это: "А сложу-ка я все функции для работы с БД в один пакет, а обработку HTTP запросов - во второй".

Проблема стоистой архитектуры в том, что она очень плохо подходит для написания таких бизнес приложений. Потому что все функции и структуры, которые используются вместе лежат на разных слоях. Но при этом в каждом слое лежит набор функций, которые никак не связаны.

95% времени моя программистская работа такая: мне нужно исправить баг, доработать какую-то функциональность или сделать новую. Во всех случаях грубо говоря это какой-то один API вызов. И в идеале я должен открыть один файл и там должен быть весь код, который мне нужен в текущий момент. Понятное дело, что это только в теории возможно, так как иначе утонем в копипасте, но открывать 10 папок и по ним перемещаться очень неудобно.

В небольших приложениях любой вызов API работает по схеме handler.CreateUser -> businessLogic.CreateUser -> repo.CreateUser [-> dao.CreateUser] За исключение 5-10 "библиотечных" функций типа GetUserByID. В них вообще слоеная архитектура вредна, получается. В крупных проектах "библиотечных" функций гораздо больше получается, но все-равно слоеную архитектуру ну никак не получается назвать идеальным решением.

Слоистая архитектура противоречит принципу Low Coupling - High Cohesion

Если я правильно понял о чём вы говорите. Согласитесь читать 1 вызов API который содержит 1000 строк это не самое приятное занятие, ведь можно всё разделить(постараться) на логические компоненты по зонам отвественности, которые вы бы могли импортировать на каждом слое по мере необходимости и проваливаться в более сложные частички кода. А не хранить это всё в одной "Ручке".

Для примера представьте вы наняли нового разработчика к себе в команду, он открывает код, а у вас в Presentation Layer весь код вашего приложения. Поверьте это сложно читаемый код и никакое переименование переменных не поможет в этой ситуации. А если бы это было разбито на "папочки и файлики", новый разработчик более быстро погрузился в процесс разработки.

Мой главный посыл был в том что стоит уделять время на организацию кода, так как это важно, а Layer Architecture не панацея как и другая архитектура.

Опыт подсказывает, что очень часто одна функция в 1000 строк делится на 2 функции по 800 строк или на 4 функции по 600 строк. Проще одну прочитать. В целом если весь код писать в presentation то тоже можно аккуратно разбить на функции, которые все будут лежать в одном пакете.

Когда новый разработчик приходит ему наоборот, сложно окинуть взглядом весь код, проще, если он будет погружаться постепенно, по функциональным частям а не по слоям.

Мне кажется тут полностью текут абстракции, слоенность нарушена, не пахнет какой-либо архитектурой, а статью писал технический писатель.

Почему в слое бизнес логики есть понятие Database? session? EntityDatabaseModel? Это все детали конкретной реализации инфраструктурного слоя, если быть точнее - orm/sql специфика. У вас могут entity создаваться через http запросы, к примеру, и вам придется все переписывать.

С нормальным расслоением у вас будет EntityRepository(к примеру) интерфейс, который можно реализовать как DatabaseEntityRepository, где в методе create уже будет вся специфика базы данных - как создание session, всякие ОРМ модели и тп. А можно реализовать как HttpEntityRepository, где уже своя специфика - работа с json и обработка статус кодов. В вашем коде все переплетено сильным coupling, слой бизнес логики зависит от слоя инфраструктуры(должно быть наоборот, если что), а DI и не пахнет.

В настоящей слоеной архитектуре слой бизнес логики не зависит от других слоев, все зависят от него. Из других слоев ничего не импортируется, а все слои зависят от слоя бизнеса, принимая и возвращая его сущности, а также реализуя его интерфейсы.

Покажу на примере, сначала ваш код:

def create(dto: bll_schemas.DTO()) -> bll_schemas.dto:
  with database.start_session(): # Это не бизнес логика, sql специфика
    if database.get(**object):  # специфика базы данных
      raise bll_exc.DuplicateError("Object is already created")  # ошибка ОРМ
    database_entity = database_models.Entity(**object.model_dump())  # ОРМ модель
    database.create(session=session, object=database_entity)  # вызов ОРМ
    return self.service_schema.from_orm(database_entity)  # слой представления знает о слое архитекртуры

И как это могло быть:

def business_create(dto: EntityDomain, repo: EntityRepo) -> EntityDomain:  # EntityRepo - интерфейс, можно послать любую реализацию
    try:
        res = repo.create(dto)
    except DuplicateError:  # бизнес ошибка
        ...
    ...
    return res
class SQLEntityRepo(EntityRepo):  # SQL-пример репозитория, EntityRepo определен в бизнес слое как абстракция для реализаций

    def create(self, dto: EntityDTO) -> EntityDTO:
        q = database_models.Entity(**object.model_dump())
        try:
            session.add(q)
            session.commit()
            session.refresh()
        except DatabaseException as exc:
            session.rollback()
            raise DuplicateError  # Рейзим именно бизнес ошибку
        return dto
    
    
class HTTPEntityRepo(EntityRepo):  # Http-пример репозитория
    
    def create(self, dto: EntityDTO) -> EntityDTO:
        res = requests.post("create-url", data=dto.dict())
        
        if res.status_code == 409:
            raise DuplicateError
        return EntityDTO(**res.json())

Детали реализации мы прячем, к примеру бизнес кейс "DuplicateError" в случае sql - это IntegrityError, а в случае http - статус код и какой-то текст в json. Бизнес слою об этому знать не надо, репозиторий обрабатывает все и выдает уже бизнес ошибку DuplicateError. В вашем же коде почему-то бизнес обрабатывает IntegrityError конкретной ORM. С кодом выше - именно бизнес слой изолирован, у нас есть интерфейс для инфраструктурного слоя, который можно реализовать как угодно, в тестах можно через DI протестировать бизнес логику, послав специальный тестовый репозиторий и т.д.

Все максимально книжно и утрированно, в реальном мире есть побольше абстракций - всякие Gateways, Interactors, UoW и тп, но для примера - это и есть слоенная архитектура.
Если честно, не понимаю, как можно прочитать хотя бы одну из книг по теме и написать то, что написано в статье. А у вас подача, словно вы перечитали их много, вот ваша цитата: "Думаю многие читали кучу книжек по поводу Hexagonal, Onion, Clean, Layer Architecture".

По поводу нормального расслоения, мне хотелось в статье скорее сделать акцент на то что на организацию кода необходимо уделять время чтобы потом не бить шишки, и дал слоёную архитектуру как базу от которой можно оттолокнуться. Поэтому не планировал описывать более детальное расслоение(UOW, Repository и тд.) в будущем учту что раз взялся за тему, то более детально всё описывать. Спасибо за комментарий очень полезный, надеюсь разработчики воспользуются этим дополнением к статье.

Парадокс в том, что Layer Driven Architecture находится в противоречии с Domain Driven Architecture в смысле организации кода. С дной стороны, хочется чтоб отдельно и рядышком были тут components, тут services, тут templates и т.д. а с другой хотелось бы чтоб вот тут были User, Company, Client и т.д. и там внутри всё, что с ним связано.

Когда и первого и второго много (т.е. типичный проект средней сложности) возникает вопрос организации этого хозяйства и тут, на мой взгляд, плохи только крайности, а выходом является волевое решение в виде некоторого компромиса.

Например, как вариант, естественную иерархию domain сущностей внутри упорядочивать по плоской фиксированной layer driven структуре. Сложно сказать, какая форма порядка самая лучшая, но точно можно сказать, что если ничего такого не делать, то с точки зрения Evolutionary Driven Architecture о временем отсутствие какого-либо порядка со всей неизбежностью уткнет проект в п.24

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории