KISS Architecture. От микросервиса до монолита

Андрей Копылов, наш технический директор, рассказывает, какой подход к проектированию архитектуры приложений использует команда веб-разработчиков AREALIDEA, и, чем KISS Architecture, его собственная разработка, так хороша.



Существует масса подходов к проектированию архитектуры приложения. MVC, DDD, Clean Architecture и множество других.


MVC хорошо подходит для маленьких приложений. При попытке масштабирования MVC превращается в самую распространенную архитектуру в мире IT — Большой ком грязи.


DDD отличная архитектура, но ее никто не понимает. Разве что сам создатель и пара архитекторов. Цель же архитектуры в том, чтобы она была понятна каждому разработчику.


Clean Architecture отличная архитектура, но ее полная реализация имеет смысл для огромных приложений. Для малых и средних мне она показалась слишком сложной.


Современные тенденции — переход к сервисам и микросервисам — на этом фоне Clean Architecture становится чересчур тяжеловесной.


Казалось бы тогда давайте возьмем MVC для микросервиса и на этом и остановимся. Но нет, такой велосипед нам не подходит.


Составляющие


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


Вот компоненты, которые нужны для создания понятной и удобной структуры:


  • Routers
  • Controllers
  • Views
  • Services
  • Models

Слои


Router


Router отвечает за маршрутизацию запросов. Размер роутера и их количество косвенно говорит о размере вашего приложения. Для большого монолитного приложения может быть более одного слоя роутеров.


Router присутствует в любой архитектуре, но часто в неявном виде. А поскольку явное лучше неявного, то стоит его вытащить наружу — сделать составной частью архитектуры.


Controller


Контроллер является прослойкой между роутером и сервисами. Бизнес логики в контроллере быть не должно.


Каждый контроллер управляет только одной сущностью. Если нужно больше сущностей, то нужно добавить еще один контроллер.


Количество и размер контроллеров косвенно говорит о размере вашего приложения. Вертикальный слой под контроллером можно выделить в отдельный микросервис.


Views


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


В предельном случае View представляет собой JSON, XML и подобные форматы.


Services


Только Сервис может содержать бизнес логику. Сервис обычно обращается только к одной модели. Сервис может вызвать другой сервис.


Слой сервисов разделяем на Commands и Queries (команды и запросы). Это стандартный подход для CQRS.


Один сервис выполняет только одну функцию. Приватных функций может быть сколько угодно, а публичная только одна. Название сервиса начинается с глагола. Примеры: GetUsers, GetPostById, UpdateUser, PublishPost. Именно название сервиса намекает на правильное разделение функционала.


В Queries помещаем сервисы, которые не изменяют базу данных. Query содержит одну публичную функцию get. В Commands помещаем сервисы, которые изменяют БД. Command содержит одну публичную функцию execute.


Models


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


Если модель работает с БД, то одна модель обслуживает только одну или несколько таблиц.


Рецепты


Микросервис


Микросервис в моем понимании должен управлять только одной сущностью. Поэтому архитектура для простейшего микросервиса будет выглядеть таким образом:


  • один Роутер;
  • один Контроллер;
  • несколько Views;
  • несколько Сервисов;
  • одна Модель.


Сервис


Сервис — это мини приложение. Оно содержит:

  • один Роутер;
  • несколько Контроллеров;
  • несколько Views;
  • несколько Сервисов;
  • несколько Моделей.


Монолит


Монолит — это большое приложение. Монолиты никто не любит по причине их монструозности. Монолит оправдан, если следовать подходу Monolith first. В таком состоянии ваше приложение может пребывать довольно долго.


Монолит содержит в себе:


  • один СуперРоутер;
  • несколько обычных Роутеров;
  • много Контроллеров;
  • много Views, много Сервисов;
  • много Моделей.


Это начинает выглядеть немного страшно. Здесь явно видно дополнительное вертикальное разделение слоев. Это позволяет оставаться приложению пока еще управляемым и поддерживаемым. Распиливание монолита на части становится чисто механической задачей.


Для сохранения стройности архитектуры нужно:


  1. Добавить один верхнеуровневый роутер, который будет разруливать глобальные пути — СуперРоутер.
  2. Распределить файлы в структуре помодульно. То есть в соответствии с будущим распилом на отдельные сервисы.

Тестирование


В рамках рассматриваемой архитектуры тщательному тестированию подлежат только сервисы — только в них заложена бизнес логика. А мокать нужно только Модели.


Если у вас возникает желание протестировать, что-то кроме сервисов, то вероятно место для логики выбрано неправильно.


Заключение


На мой взгляд, KISS Architecture подходит для 80% проектов и обеспечивает плавную эволюцию проекта.


Этот архитектурный подход будет понятен всем разработчикам и для его применения на практике не нужно читать толстенные книги про DDD.

Поделиться публикацией

Похожие публикации

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

    +4
    Сервис

    Сервис — это мини приложение. Оно содержит:

    один Роутер;
    несколько Контроллеров;
    несколько Views;
    несколько Сервисов;
    несколько Моделей.


    Меня одного здесь что-то смущает?
    И как View и Controller оказались в одном слое, если даже задачи у них принципиально разные.
    И да, Clean architecture здесь вообще не при чем, нет в ней никаких царь-роутеров и царь-сервисов
      0
      И как View и Controller оказались в одном слое, если даже задачи у них принципиально разные.

      Задачи разные да.
      Они находятся в одном слое с точки зрения отдаления от пользователя.
        –1
        1) архитектура кода не строится исходя из точки зрения отдаления от пользователя
        2) пользователь view может увидеть, а контроллеры — нет.
        3) слой — состоящий из принципиально разных элементов — или не слой, или дилетантская ошибка.
        Буду честен — статья о классическом представлении Layered pattern, как проектировали приложения десятилетиями. Какую-нибудь толстую книгу о проектировании все-таки стоит прочесть, иначе вы правда не поймете, почему сейчас архитектуру, подобную вашей, пытаются избегать.
          0

          Ок, а какие на данный момент best practices?

            +1
            Сложный вопрос. По мобилкам это MVVM и Clean (только настоящий, здесь никакого Clean Architecture нет)
            Вопрос не в best practice, Layered pattern с монолитом обладает кучей недостатков, с которой в разной степени успешности борются другие архитектуры.
            Выражение «KISS Architecture подходит для 80% проектов и обеспечивает плавную эволюцию проекта» подходит для любой архитектуры, и даже для ее отсутствия — все будет от пряморукости разработчика зависеть)
              +2
              здесь никакого Clean Architecture нет

              Все так. Тег выбран неправильно.
              Этот «архитектурный» подход ближе всего к MVC с введением слоя Сервисов. Который в свою очередь вертикально разделяется на Query и Command.
              Ну и фишка этого подхода в том, что Query или Command выполняет только одну задачу. Это позволяет не иметь конфликтов при разработке.

              Собственно вся суть подхода описана в этом комменте.
                –1
                Не уверен, что это поможет — при мерже, проблем, несомненно, будет меньше, а вот размазывание кода на много мелких сущностей с одним методом — хорошая заявка на «забыл внестив изменения во все необходимые классы»
                  0
                  Нельзя исключать такой возможности. Но в нашем случае результаты пока положительные.
                    +1
                    Вообще не понятно, что здесь взято не из MVC.
                    Clean — не сложный, он то как раз гораздо понятнее и популярнее, чем описаные выше правила.
                  0
                  По хорошему, в MVC как раз подразумевается, что модель — это набор методов, фасад, прячущий за собой бинес-логику, инфраструктуру и т. п., а не какие-то объекты типа ActiveRecord, которіми контроллер оперирует.
                    0
                    Наверное да. Но на практике я в модели часто видел работу с БД. Да и сам так делал.
                      +1
                      Работа с БД должна быть в модели в MVC (не путать с моделью DDD), но факт наличия БД в ней не должен протекать в контроллер и вью.
          +7
          MVC — это паттерн UI слоя :)
          +3
          > Каждый контроллер управляет только одной сущностью. Если нужно больше сущностей, то нужно добавить еще один контроллер.

          Вот реально так? На примере абстрактного интернет-магазина: есть сущность «заказ», есть сущность «адрес доставки», есть сущность «строка заказа» (товар, количество) — и как минимум последняя не имеет никакого смысла вне первой. Зачем для неё отдельный контроллер?
            0
            Вот реально так? На примере абстрактного интернет-магазина: есть сущность «заказ», есть сущность «адрес доставки», есть сущность «строка заказа» (товар, количество) — и как минимум последняя не имеет никакого смысла вне первой. Зачем для неё отдельный контроллер?

            Тут тоже наверное не вполне адекватно сформулировал.
            Отдельный контроллер будет для сущности Заказ. И отдельный для сущности Товар.
            Дальше дробить пожалй нет смысла.
              +2
              В толстой книжке по DDD такие сущности называются агрегатами. Только ради общепринятого именования их уже стоит читать.
            +1
            Лично я пишу бизнес логику в сервисах уже несколько лет. Но мне кажется, что такое чрезмерное разделение на один «invoke» — как то через чур. Скажите, какая причина такому делению?
              0
              При большом количестве разработчиков (больше 2-х) и длинных релизах очень много конфликторв было.
              Сейчас конфликтов практически нет.
                +3
                «У нас было много разработчиков и мало файлов, мы сделали много файлов и теперь все ок»
                Вам не кажется что где-то что-то пошло не так на этапе распределения задач?
                  0
                  1. Не всегда возможно распределить задачи так как хочется. Нам приходится сталкиваться с суровой действительностью, в которой Заказчик может диктовать собственные правила.
                  2. В Clean Architecture есть Use Case, который как раз отвечает только за одну фунцию. По крайней мере именно так я понял.
                    0
                    1. Вам заказчик диктует, кто будет зададчку решать? Интересный феодализм
                    2. Скорее там речь о типе задач. И гарантируется он не просто «один класс — одна функция», а круговой моделью взаимодействия. Данная модель гарантирует, что презентер не стучитсяв модель или воркер например.
                      0
                      Заказчик дикутет количество задач, которые должны выполняться одновременно. А еще он диктует в какой последовательности и когда будут сдаваться те или иные фичи.
                      Бооооль.
                  0
                  Немного странно. По идее количество конфликтов в ситуации «много классов(файлов) с одним методом» и «один класс(файл) с много методов» должно быть лишь немного меньше, если по методам аналогично функциональность распределена. Или это фикс проблемы из разряда «давайте перейдём на микросервисы, чтобы дергали только фасад модуля, а не его напрямую»
                +1
                Router=> Controller=> Service=> DAL
                It does not matter how you call it, it is just old good architecture
                  +1

                  Пока отдельный компонент (напр. микросервис) достаточно мал и прост (условно говоря, если полезного кода в микросервисе до 500 строк — за вычетом стандартно-инфраструктурного кода вроде сетевого I/O, сериализации, логов/метрик), то ему вообще никакая явная архитектура не требуется. Может иметь смысл структурировать такие компоненты любым общепринятым в компании способом, просто чтобы быстрее находить нужный код в любом компоненте, но не более того, и чем меньше формальных требований предъявляет такая структура — тем лучше. Описанное в статье ближе всего именно к такому способу структурирования, хотя и избыточно раздутому, на мой взгляд.


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


                  А вообще всё это мелочи, вопросы архитектуры внутри компонентов в наши дни проработаны достаточно хорошо. Сложная часть в архитектуре начинается там, где нужно разделить весь проект на вышеупомянутые компоненты, чтобы большинство из них было достаточно небольшими, и чтобы связи между ними при этом оставались ясными и эффективными. И вот эта задача в статье вообще не упоминается. И большой комок грязи возникает не только из MVC, он вполне может получиться и из Clean, если не разделить большой проект на компоненты — просто в случае Clean это будет несколько больших комков грязи, тщательно разделённых интерфейсами. :)


                  Всё вышеописанное касается бэкенда. Для UI зачастую MVC/MVVM подойдёт лучше.

                    0
                    Секунду, давайте по порядку.

                    MVC хорошо подходит для маленьких приложений

                    Очень спорное утверждение. Ведь паттерн описывает то, как представление должно взаимодействовать с бизнес-логикой приложения и/или на оборот (в некоторых фокрах MVC).

                    DDD отличная архитектура, но ее никто не понимает.

                    ddd — это не архитектура, это подход (методология) для разработки доменной модели.

                    Clean Architecture отличная архитектура, но ее полная реализация имеет смысл для огромных приложений


                    Конечно писать обычный бложик с использованием «чистой архитектуры» не очень хорошая идея. Но Clean Architecture отлично подходит для отделения мух от котлет даже в небольших бизнес-приложениях.

                    MVC

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


                    Каждый ваш микро-сервис это приложение, которое построено с использованием MVC-паттерна. От которого вы предлагаете отказаться выше:
                    Казалось бы тогда давайте возьмем MVC для микросервиса и на этом и остановимся. Но нет, такой велосипед нам не подходит.


                    Итого

                    На мой взгляд, KISS Architecture подходит для 80% проектов и обеспечивает плавную эволюцию проекта.


                    На мой взгляд ваша «KISS Architecture» — это обычная через чур раздробленная микросервисная архитектура каждый компонент которой построен на MVC. И не более того. Применять ее для простых приложения нет ни какого смысла — я с удовольствием сделаю выбор в пользу монолита и откажусь от кучи накладных расходов на тестирование и развертывание. А если применять вашу «архитектуру» к крупным приложениям, то вы просто погрязнете в лавине ваших моделек-контейнеров.

                    Этот архитектурный подход будет понятен всем разработчикам и для его применения на практике не нужно читать толстенные книги про DDD.


                    Очень хочется, что бы разработчики понимали главное — нет «серебряной пули». Каждый проект уникален и требует индивидуального подхода. Конечно если ваша компания не занимается массовой разработкой бложиков на микросервисах :).

                    А книги читать надо, в том числе и про ДДД.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое