История одного удачного применения SPR в Legacy проекте

В этой статье я расскажу на примере эволюции своего проекта историю перехода и видение контрактного программирования.

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

Что-то я буду описывать своим языком.

История моего перехода к контрактному программированию началась с того, что у меня был разросшийся legacy проект с большим набором разной функциональности в рамках одной бизнес-области. Плюс над всем этим была ещё многопоточность и кастомизация под разных клиентов. Если говорить конкретно, то бизнес-областью был обмен электронными юридически значимыми документами между учётной системой, файловой и облачными сервисами.

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

Визуально такую архитектуру можно представить в виде нескольких вертикалей функциональности

image

Характерной особенностью такой архитектуры было то, что если появлялось какое-то дополнительное требование или ошибка, то реализовать такую задачу требовалось во всех «функциональных модулях» системы, а не в одной области. Ещё одной характерной чертой было наличие в проекте большого количества builder-ов, которые и приводят к такому вертикальному разделению бизнес-области. Разработчик реализует новые алгоритмы новых бизнес-требований в коде строителей, что приводит к появлению новых сущностей с разными структурами для одного и того же бизнес-объекта. Надо признать, что такой подход можно считать более объектно-ориентированным чем простой code-behind подход со спагетти-кодом. Таким образом если вы видите в своих проектах большое количество строителей, то переход к контактному подходу упростит поддержку и ускорит развитие вашего проекта.

Однажды, мы в компании столкнулись с тем, что клиент, у которого мы выполняли внедрение, стал высказывать, что система, которую я разрабатывал, часто выдавала ошибки. Я не мог с этим поспорить, хоть ошибки периодически появлялись, но они устранялись и у части клиентов система стояла и ей пользовались. Надо сказать спасибо, что клиент был вредный и придирчивый, и очень тщательно тестировал нашу систему в период внедрения.

Самому мне тоже не нравилось то, что у меня не хватает времени на развитие и поддержку, не смотря на большое количество усилий и я в какой-то период просто не представлял, что ещё может улучшить систему и сделать её стабильнее.

Решение пришло внезапно и неожиданно.

Я хорошо понял, что в разных модулях системы по разному реализованы одни и те же бизнес-объекты — документы разных видов, квитанции, воркеры, клиенты сервисов, клиенты бд, файловые клиенты с возможностью хранения документов в различных форматах (кастомизация под разных клиентов ;) )

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

Тогда я решил — надо создать что-то вроде единого конструктора с деталями, из которых можно собрать все алгоритмы, реализованные в системе. Что логично, ведь это уменьшает количество схожих классов, и упрощает поддержку.

Я решил максимально строго разделить «вселенную» моего приложения на

  • дата-контракты — структуры данных, которые содержат и используются только для хранения данных

    Документы
    Конверты
    Квитанции
    ЭП

  • клиенты-сервисов — специально пишу что клиенты сервисов, так как при реализации каждую систему, с которой происходило взаимодействие в бизнес-области я представлял как объёртку над данной системой, в которое были реализованы методы по работе с дата контрактами. И помимо того, что эти классы являются обёртками над службами, так же они содержали в себе необходимую связывающую логику, конвертации и преобразования, чтобы упростить бизнес-логику

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

Например
CloudClient — клиент облачного сервиса, на который отправляются и обрабатываются документы
— Загрузить
— Отправить
— Получить
— Скачать
— Согласовать


FileSystemClient — клиент файловой системы
ERPClient — клиент учётной системы

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

А выделение схожих по функциональности методах в соответствующих клиентах приводило к большей специализации и повторному использованию кода.

Архитектура теперь представляла собой схему как на картинке

image

Все базовые объекты моего конструктора я выделил в сборку с окончанием SDK (Software Developers Kit) и работать стало легче и самое главное приятнее. Работа уже перестала представлять из себя создание костылей и временных решений, а теперь отношение к проекту изнутри стало серьёзнее от того, что я теперь видел огромный запас масштабируемости и конкурентности архитектуры. Это к тому, что такой факт значительно увеличил мотивацию и открыл новые профессиональные горизонты.

На базе созданной SDK я сдал реализовывать слой бизнес-логики, автоматизирующей реализацию непосредственно бизнес-задач. Так как все технические нюансы были выведены в более низшие слои, находящиеся в клиентах, то реализация тех же самых алгоритмов упростилась и стала выглядеть намного приятнее.

Подход прост, и позволяет упростить дальнейшую разработку и что очень важно — увеличить гибкость архитектуры. Да, для этого потребуется выполнить масштабный рефакторинг. В моём случае потребовалось 2 недели разработки в фанатичном режиме. Зато после, я почти не кодил, потому что всё стабильно работало.

А благодаря разделению ответственности (SPR), и логам, я смог быстро пресекать ошибки сторонних сервисов, влекущих ошибки на моей стороне. Раньше на это требовалась аналитика, съедавшая значительное время.

Такой подход хорошо использовать на определённых слоях. На более высоких слоях приложения возможно будет более удобна анемичная модель, только реализовать анемичную модель лучше на базе контрактного подхода.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

    0
    без конкретных примеров всей прелести нового подхода не понять. примеров бы.
      0

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

      0

      Вы уверены, что это должно быть в хабе "Системное программирование" ?


      system programming

        0

        Возможно поторопился)

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

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