Следить за обновлениями блога можно в моём канале: Эргономичный код Эпиграф a class is a necessary but insufficient vehicle for decomposition Класс - это необходимое, но недостаточное средство декомпозиции — Grady Booch, Object-Oriented Analysis and Design with Applications Количество классов в реализации даже небольшой программы на один человеко-месяц исчисляется десятками. В средних программах на несколько человеко-лет счёт идёт уже на тысячи. А человек может одновременно оперировать 7-ю +/- 2 объектами. Поэтому все нетривиальные программы требуют декомпозиции своей реализации на более крупные блоки, чем классы - я буду называть такие блоки пакетами. Сейчас наиболее распространены два основных подхода к декомпозиции систем: пакетирование по слоям и техническим аспектам (далее просто "по слоям" для краткости) пакетирование на основе предметной области, представленное группой вариантов: пакетирование по фичам пакетирование по компонентам ограниченные контексты и пакетирование по агрегатам из предметно-ориентированного дизайна (DDD) Однако ни один из этих подходов мне не подошёл в полной мере и я изобрёл…​ объектно-ориентированный подход к декомпозиции систем. Точнее, я изобрёл простую методику выполнения декомпозиции, а потом понял, что на выходе она даёт штуки обладающие свойствами объекта. Но обо всём по порядку - сначала я рассмотрю критерии оценки подходов, распространённые подходы и почему они мне не подошли. А закончу пост представлением методики выполнения объектно-ориентированной декомпозиции. В области группировки кода вообще и классов в частности есть ещё много разных идей и подходов - разделение Парнаса, структурный дизайн Константина, принципы дизайна пакетов Мартина, подходы на основе кластерного анализа. Однако на основе того что я вижу в реальном коде и интернете - все они не получили распространения и, чтобы сократить и без того огромный пост, я не буду их рассматривать. Отмечу лишь, что две из них (разделение Парнаса и структурный дизайн) дали нам универсальные критерии оценки дизайна программ: сокрытие информации и сцепленность/связанность соответственно. И я их возьму за основу для критериев оценки подходов. Подходы я буду сравнивать по двум аспектам - насколько хорошие декомпозиции они дают и насколько легко их применять. Качество декомпозиции я буду оценивать по следующим критериям: Сокрытие информации. Какую информацию подход скрывает Сцепленность. Какое количество связей между пакетами порождает подход Связанность. Какое количество связей внутри пакета порождает подход Масштабируемость. Насколько большие системы могут быть эффективно декомпозированы с помощью подхода Оценка сцепленности только по количеству связей внутри пакета, может быть легко "хакнута" - например, если в слоёной декомпозиции слой сервисов сделать высоко сцепленным. В этом случае формально слой сервисов будет обладать высокой связанностью, но фактически он будет обладать высокой сцепленностью внутренних элементов. Для того чтобы это обойти, сцепленность я дополнительно буду оценивать по локальности изменений. Локальность изменений, в свою очередь, я буду оценивать по двум критериям: Сколько в среднем пакетов затрагивает одно изменение. В идеальной декомпозиции это число должно быть равно 1. Можно ли найти топологическую сортировку, определяющую такой порядок, что при удалении пакетов в соответствии с ним, система продолжает на каждом шаге: Собираться Быть полезной для конечного пользователя Что касается лёгкости применения, то её я буду оценивать по таким критериям: Простота обучения. Насколько просто обучить человека подходу. Простота исполнения. Есть ли у подхода методика выполнения и насколько она проста Картинки кликабельны В этом посте я буду рассматривать только декомпозицию выделенных бэкендов, которые предоставляют API для фронтэнда. В этом случае контроллеры выступают в роли тонких адаптеров обращений по протоколу сетевого взаимодействия (REST, SOAP, всевозможные протоколы RPC и т.д.) в вызовы методов сервисов. Иллюстрировать декомпозиции я буду на примере вымышленной системы управления задачами. Пример Этот подход настолько прост и существует настолько давно, что, кажется, это уже коллективное бессознательное нашей индустрии. Судя по всему, своими корнями он уходит в статью Дейкстры The structure of the “THE”-multiprogramming system, датированную 67 годом, что делает его одним из самых ранних подходов. Несмотря на свой возраст, этот подход является самым распространённым и по сей день и, думаю, он знаком всем разработчикам без исключения. Способ группировки заключается в том, что команда выбирает несколько аспектов реализации (самые частые примеры - контроллеры, сервисы, репозитории/дао, сущности, дто, фабрики, исключения, перечисления) и группирует классы по ним. Классы, которые не удаётся однозначно отнести к одному из этих аспектов, сваливают в специальную группу, которую обычно называют utils или common. В слоёной архитектуре существует только одно ограничение - более "низкие" слои не могут зависеть от более "высоких". Это ограничение становится сложнее соблюдать, когда на одном уровне смешивают и архитектурную декомпозицию (контроллеры, сервисы, репозитории, сущности) и техническую (интерфейсы, исключения, перечисления). В этом случае для технических "слоёв" невозможно определить их порядок: что является более "высоким" слоем/уровнем - исключения или перечисления? Такая декомпозиция теоретически должна скрывать способ реализации технических аспектов, например, способ работы с БД. Однако на практике, детали реализации слоёв очень часто "протекают" через границы, результатом чего становится отсутствие какого бы то ни было сокрытия информации вообще. Для слоёной декомпозиции естественной является высокая сцепленность системы. Самый "толстый" слой сервисов содержит в себе только бизнес-логику, а все структуры данных (сущности, дто, исключения, перечисления) и вспомогательный код для работы с ними (репозитории, фабрики, билдеры) находятся в других пакетах. В итоге каждый класс в пакете сервисов начинает зависеть от множества классов в соседних пакетах, тем самым, по определению, повышая сцепленность. Кроме того, даже единственное ограничение на зависимости между слоями чаще нарушают, чем соблюдают, ещё больше повышая сцепленность системы за счёт внесения циклов в зависимости. В итоге декомпозиция по слоям представляет собой сочетание врождённой высокой сцепленности между пакетами и белого пятна в проектировании внутри пакетов. Эта гремучая смесь приводит к превращению системы в печально известный Big Ball of Mud (большой ком грязи) уже к концу первого года своей жизни. Если связанность оценивать только по количеству связей внутри пакета, то откровенно плохо реализованная система с десятками зависимостей в каждом сервисе и связным графом сущностей может показаться высоко связанной. Однако истинная связанность таких систем легко демонстрируется с помощью дополнительных критериев, введённых специально для этого случая. Большинство нетривиальных изменений таких систем будет затрагивать множество пакетов. А удаление единственного пакета (контроллеров), которое не сломает сборку, сразу же сделает всю систему бесполезной для пользователя. С точки зрения масштабируемости слоёная декомпозиция также даёт не лучший результат. Постоянно развиваемая система довольно быстро доходит до 20-30 классов одного типа (то есть в одном пакете) и снова возникает проблема их группировки. Хорошо, если команда осознанно выбрала слоёную декомпозицию, для сокращения времени разработки первой версии. В этом случае, достигнув пределов масштабирования слоёной архитектуры, команда может провести качественную декомпозицию. Однако на практике слоёную декомпозицию не выбирают. Чаще всего это единственный известный и понятный разработчикам способ декомпозиции. И разработчиков сложно в этом винить, декомпозиции систем действительно нигде не учат - меня самого не учили в университете, и соответствующих курсов я ни разу не видел. С этим мнением согласен и Джон Оустерхаут, автор A Philosophy of Software Design: I have not been able to identify a single class in any university where problem decomposition is a central topic. We teach for loops and object-oriented programming, but not software design. У меня до сих пор не получилось найти хоть один курс в каком-либо университете, где бы декомпозиция задач была центральной темой. Мы учим циклам и объектно-ориентированному программированию, но не проектированию ПО. — John Ousterhout, A Philosophy of Software Design, с. 9 Поэтому, как правило, дальнейшая декомпозиция внутри пакетов-слоёв выполняется методом "как бог на душу положит" первым разработчиком, который решил, что "пакет слишком разросся". Если слоёная декомпозиция даёт столь плохие результаты, как она стала самой распространённой? Секрет кроется во второй группе критериев оценки методики - простоте обучения и исполнения. Слоёная декомпозиция не требует никакой квалификации или интеллекта и может быть успешно автоматизирована даже без применения слабого ИИ - просто путём поиска нескольких ключевых подстрок в строке определения класса. Этим же определяется и простота исполнения - опытный разработчик выполняет слоёную декомпозицию буквально спинным мозгом, не затрачивая на это ни секунды времени. Хочу отметить, что я не являюсь противником разделения кода на слои как такового - в моих проектах есть и контроллеры, и сервисы, и репозитории. Более того, во всех моих системах последних семи лет за пользовательский интерфейс, бизнес-логику и хранение данных отвечают разные программы - веб- или мобильное приложение, бэкенд сервер и СУБД соответственно. А если бы я делал программу, реализующую все три аспекта, то я бы её в первую очередь разбил по слоям. Однако для программ, сфокусированных только на одном из этих аспектов, слои работают откровенно плохо. Итоговая оценка пакетирования по слоям (по пятибалльной шкале): Сокрытие информации - 2 (неуд.) Сцепленность - 2 (неуд.) Связанность - 2 (неуд.) Масштабируемость - 2 (неуд.) Простота объяснения - 5 (отл.) Простота применения - 5 (отл.) Пример. Найти оригинальный источник идеи пакетирования по фичам у меня не получилось, но этой теме посвящено множество постов: Package by feature, not layer Package by Layer vs Package by Feature How Changing Java Package Names Transformed my System Architecture Package by Feature, Not by Layer Package by Feature Is Demanded Хотя ни один из них я не могу назвать ни авторитетным, ни исчерпывающим. В этом подходе, приложение декомпозируют на пакеты по фичам - для каждой фичи создаётся пакет, и весь код, реализующий фичу, попадает в него. Притом каждый пакет имеет явно выделенный публичный интерфейс, а всё остальное скрывается. И тут мы сразу упираемся в главный недостаток этого подхода - его сложно объяснить, а исполнить ещё сложнее. Декомпозиция по фичам только звучит просто (и то не для всех). Когда же вы сядете и попытаетесь декомпозировать систему по фичам, у вас тут же возникнет множество вопросов: "А фича - это вообще что такое?", "Как мне из требований получить набор фич?", "Судя по примерам, фича - это таблица. Мне что, заводить по пакету на каждую таблицу?", "А что делать с таблицами связками?", "Что делать с функциями, которые затрагивают две и более таблицы - в какой пакет их помещать?", "А что делать с функциями, которые работают не с таблицами, а с REST API?", "А с S3?", "А куда мне положить DSL создания Excel файлов для нескольких фич? В utils?". Ответы на все эти вопросы придётся искать самостоятельно, потому как все посты ограничиваются поверхностным описанием идеи. Найти ответы, конечно же, можно - я нашёл и в итоге у меня получился объектно-ориентированный подход к пакетированию. Но мне для этого потребовалось пять лет вялотекущих размышлений, два года активной работы в этом направлении и эксперименты в пяти коммерческих проектах. Не у всех есть желание и возможность этим заниматься - когда давят сроки, лучше декомпозировать на въевшиеся в подкорку слои. Но если преодолеть все сложности и не остановиться на полпути, то наградой будет декомпозиция высокого качества по нашим критериям - принципы низкой сцепленности/высокой связанности и сокрытия информации практически во всех постах из списка предлагаются как главенствующие. С масштабируемостью дела обстоят хуже. При декомпозиции по фичам быстро расти будет не количество классов в пакетах, а количество самих пакетов. И если ничего не предпринять, то уже количество пакетов быстро дорастёт до 20-30 штук и проблема декомпозиции системы снова встанет в полный рост. В постах же эта проблема либо не упоминается вовсе, либо упоминается лишь вскользь. В результате разработчик снова остаётся с ней один на один. Но благодаря поискам ответов на изначальные вопросы, разработчик хорошо прокачает свой скилл проектирования. И в этом случае получившаяся декомпозиция вполне вероятно окажется высокого качества. Итоговая оценка пакетирования фичам: Сокрытие информации - 4 (хор.) Сцепленность - 4 (хор.) Связанность - 4 (хор.) Масштабируемость - 3 (удв.) Простота объяснения - 2 (неуд.) Простота применения - 2 (неуд.) Примеры: [1], [2]. Автором пакетирования по компонентам является Саймон Браун, описавший его в посте Mapping software architecture to code (также см. 1, 2, 3, и главу 34 "Missing Chapter" из Clean Architecture). Пакетирование по компонентам очень похоже на пакетирование по фичам, поэтому я не буду на нём подробно останавливаться и лишь обозначу отличия. Браун дистанцируется от пакетирования по фичам в первую очередь тем, что у него контроллеры вынесены в отдельный пакет. По его задумке это должно повысить сокрытие информации о реализации сервисов. Однако он это делал для классических контроллеров из MVC, которые собирают модель для представления из нескольких сервисов и для нашего примера с контроллерами API в этом смысла нет. Зато есть другое отличие - модель данных выделена в собственный пакет. В тексте это явно не проговорено, но видно из иллюстрации и кода примера. И вот это уже, на мой взгляд, проблема, так как из-за этого за границы компонента начинает утекать структура его данных, и это создаёт предпосылки для сцепленности через общее окружение. Уже в процессе редактуры этого поста я наткнулся на твит Брауна: Each non-UI component isn’t a “feature”, it’s something else…​ like a domain concept or aggregate root (including DB access), integration point to the outside world, technical service, etc. Каждый "non-UI" компонент не является "фичей", это что-то другое…​ как концепт предметной области или корень агрегата (включая доступ к БД), точка интеграции с внешним миром, технический сервис и т.д. — Simon Brown, https://twitter.com/simonbrown/status/969112668132073473?s=20&t=w8c5RikLz3zFdS7X4APvNw Основываясь на этом твите, можно предположить, что подход к декомпозиции Брауна по сути совпадает с пакетированием по объектам. Но это не точно. И хотя с описанием пакетирования по компонентам дела обстоят лучше, чем с описанием пакетирования по фичам, чёткой методики выявления компонентов Браун также не предлагает. Поэтому итоговая оценка примерно такая же. Итоговая оценка пакетирования компонентам: Сокрытие информации - 3 (удв.) Сцепленность - 3 (удв.) Связанность - 4 (хор.) Масштабируемость - 3 (удв.) Простота объяснения - 2+ (неуд.) Простота применения - 2 (неуд.) Примеры: [1], [2], [3]. DDD - это полноценный подход к проектированию, описанный в одноимённой книге Эрика Эванса. Помимо этой книги, есть ещё ряд очень хороших книг - Domain Modeling Made Functional, PPP of DDD, Implementing Domain-Driven Design, суммарно на 2200 страниц. А ещё множество менее популярных книг и бессчётное количество постов в интернете. Суть подхода можно охарактеризовать как то, что исходный код программы должен быть написан на языке предметной области. Мне самому DDD импонирует и в Эргономичном подходе я позаимствовал из DDD все базовые блоки тактических паттернов. В частности, агрегаты играют одну из ключевых ролей в объектно-ориентированной декомпозиции. Однако вместо того, чтобы работать по DDD, я начал делать Эргономичный подход. Этому есть две основные причины - тяжеловесность и расплывчатость DDD. Тяжеловесность DDD проявляется как в обучении, так и в применении. DDD - это очень большая штука, на изучение которой требуется очень много времени. Как минимум надо будет прочитать 1000 страниц оригинальной книги и PPP of DDD или Implementing DDD. Мне для уверенного понимания стратегических паттернов не хватило даже прочтения всех 4 указанных выше книг по два раза (по разу от корки до корки, и ещё по разу разбираясь с отдельными концепциями). Тут строгий читатель может спросить "Какого фига ты тогда пишешь о том, чего не знаешь?". Отвечаю. Во-первых, DDD это такой слон в области проектирования, опусти я которого - другой (а возможно и тот же) строгий читатель спросит, почему я ничего не написал про DDD. Во-вторых, я критикую DDD за сложность изучения и применения - а в этом, благодаря собственному опыту, я разбираюсь как раз очень хорошо. Тяжеловесность изучения так же усложняет и исполнение - DDD требует включенности (а соответственно изучения) всей команды и экспертов предметной области. Мне в своей практике ни разу не удалось продать DDD даже команде, не говоря уж об экспертах предметной области. Касательно декомпозиции DDD предусматривает два уровня - ограниченные контексты и агрегаты. Что это такое? А вот поди разбери. A Bounded Context is an explicit boundary within which a domain model exists. Inside the boundary all terms and phrases of the Ubiquitous Language have specific meaning, and the model reflects the Language with exactness. Ограниченный контекст - это явная граница, внутри которой существует модель предметной области. Внутри этой границы все термины и фразы Вездесущего языка имеют определённое значение и модель точно отражает Язык. — Vaughn Vernon, Implementing DDD Само определение ограниченного контекста является наглядной демонстрацией сложности и расплывчатости подхода. Как декомпозировать задачу на ограниченные контексты тоже в двух словах не объяснить (мне, по крайней мере). Как вариант - границы контекста определяются языковыми границами. Осталось выяснить самую малость - где проходят языковые границы. Ещё вариант - выравнять контексты по организационной структуре компании. Но что делать, если я занимаюсь продуктовой разработкой или автоматизирую работу одного отдела? Полноценного руководства по декомпозиции ограниченных контекстов на модули DDD также не предлагает. В оригинальной книге этому посвящён целый раздел, но я бы описал его как "вода-вода, не используйте слои, вода-вода". Если не слои, то что? Ответа нет. В первой книге. Зато есть в Implementing DDD. Typically you’ll have one Module for one or a few Aggregates (10) that are cohesive, if only by reference. Обычно у вас будет по модулю для одного или нескольких агрегатов, которые связаны хотя бы по ссылке. — Implementing DDD В целом ответ хорош и в объектно-ориентированной декомпозиции, агрегаты действительно играют одну из ключевых ролей. Но он порождает три новых вопроса - что такое агрегат, как декомпозировать модель на агрегаты, как декомпозировать систему, в которой больше интеграций, чем собственного состояния? Мне чтобы найти и уложить в голове ответы на первые два вопроса пришлось проштудировать на несколько раз все книжки по DDD и потом написать пост об этом. А ответа на третий вопрос в самом DDD просто нет. Тем не менее, я полагаю, если преодолеть все сложности - "продать" подход команде и экспертам, обучить всех, изучить язык экспертов и найти в нём границы - то результирующая декомпозиция на ограниченные контексты и пакеты будет обладать высоким качеством. В частности, в силу своего фокуса на предметной области и экспертах, DDD может дать декомпозицию с наибольшей связанностью среди всех подходов. А агрегаты и полнокровные сущности помогут существенно снизить сцепленность системы и повысить степень сокрытия информации. Наконец, ограниченные контексты и возможность помещения нескольких агрегатов в один пакет дают хорошую масштабируемость декомпозиции "из коробки". Итоговая оценка пакетирования по ограниченным контекстам и агрегатам: Сокрытие информации - 4 (хор.) Сцепленность - 4 (хор.) Связанность - 5 (отл.) Масштабируемость - 5 (отл.) Простота объяснения - 2 (неуд.) Простота применения - 1 (плох.) Итак, мы пришли к выводу, что все распространённые подходы обладают существенными недостатками. Пакетирование по слоям даёт откровенно низкокачественную декомпозицию. Пакетирование по фичам и компонентам является скорее абстрактной идеей, которая требует значительных усилий по доработке для возможности эффективного применения на практике. DDD очень тяжеловесен и сложен в изучении и применении. Существует ли серебряная пуля, которая позволит нам быстро и без больших усилий выполнять качественную декомпозицию систем? Я утверждаю, что да и что она всегда была у нас под носом. И имя ей - объектно-ориентированная декомпозиция "Объектно-ориентированная декомпозиция" - это рабочее название, и я не уверен, что сохраню его. Проблема этого названия в том, что сейчас термин "объектно-ориентированный" де-факто стал обозначать "использующий классы" (хотя это совсем не то, о чём думал Кей вводя его), а у меня речь идёт про более крупные структуры, которые в коде с тем же успехом можно реализовать и на чисто функциональном языке. На рубеже 20 и 21 веков многие книги по ООП/Д/А (например, Object-Oriented Software Engineering, Designing object-oriented software, Applying UML and Patterns) в дополнение к методике проектирования классов, предлагали и рекомендации по их последующей группировке в более крупные структуры. Однако я ни разу не видел, чтобы кто-то применял эти методики в реальной жизни. Полагаю, потому что все эти методики очень тяжеловесные. Поэтому я разработал "легковесную" методику выполнения объектно-ориентированной декомпозиции. Как очевидно из названия, этот подход предполагает раскладку по пакетам разных объектов. В данном контексте под объектом я понимаю не экземпляр класса, а более крупную структуру, которая может быть реализована группой классов (группой экземпляров классов, если быть точнее). Эту структуру я называю объектом, потому что она обладает всеми присущими ему характеристиками - состоянием, которое она абстрагирует и инкапсулирует за высокоуровневым поведением. Идентичность тоже можно перенести на уровень пакетов, но на практике это требуется редко, поэтому я не стану на ней останавливаться. Идея объектов-пакетов принадлежит не мне - я её подглядел в Object-Oriented Software Engineering Ивара Якобсона (одного из соавторов UML). В этой книге Якобсон оперирует тремя видами объектов - объекты анализа, объекты дизайна и объекты (модули) языка программирования. И здесь я говорю об объектах дизайна, которые Якобсон описывает следующим образом: The design model will be composed of blocks which are the design objects. These will make up the actual structure of the design model and show how the system is designed. These blocks will later be implemented in the source code. The blocks will abstract the actual implementation. The implementation of the blocks may be one specific class in code, that is, one block is implemented by one class. However, often, a block is implemented by several different classes. The blocks are therefore a way of abstracting the source code. Проектная модель будет состоять из блоков, которые являются объектами дизайна. Они будут составлять фактическую структуру проектной модели и покажут как спроектирована система. Позже эти блоки будут реализованы в исходном коде. Эти блоки абстрагируют фактическую реализацию. Реализацией блоков может быть один определённый класс в коде, то есть один блок реализуется одним классом. Однако зачастую блоки реализуются несколькими разными классами. Таким образом, блоки являются способом абстракции исходного кода. — Ivar Jacobson, Object-Oriented Software Engineering Общая концепция ОО-подхода очень проста. Есть операции - атомарные единицы поведения, которые могут быть вызваны извне (пользователем через UI или внешней системой через [REST] API). Есть ресурсы, которые обеспечивают операции (в первую очередь коллекции в хранилищах данных, но это могут быть и файлы, и внешние системы, и внешние устройства). Операции и обеспечивающие их ресурсы надо так поделить на объекты дизайна, чтобы каждый ресурс обеспечивал операции только одного объекта. Наконец, ресурсы надо инкапсулировать в объектах дизайна - исключить возможность обращения к ресурсу напрямую снаружи объекта. Интерфейс объекта дизайна может быть дополнен операциями, необходимым другим объектам. Но в общем случае для взаимодействия объектов лучше использовать асинхронный обмен сообщениями и событиями через посредника (очередь). Кратко методика проектирования объектов дизайна (ака декомпозиции на пакеты) состоит из трёх основных шагов: Определить операции системы и ресурсы необходимые для их выполнения Сгруппировать их таким образом, чтобы с ресурсами каждой группы взаимодействовали только операции этой группы. Эти группы фактически определяют поведение и состояние объектов дизайна. Для защиты ресурсов, у каждого объекта дизайна выделяется набор классов, определяющих его интерфейс (обычно это класс сервиса и DTO). Всё остальное (сущности, репозитории, клиенты внешних систем, другие вспомогательные классы) делаются закрытыми (package private в Java, internal + ArchUnit правило в Kotlin). Часто оказывается так, что не получается однозначно отнести ресурс к определённой группе. В этом случае ресурс помещается в ту группу (А), с операциями которой он более тесно связан. А доступ к ресурсу для операций из других групп предоставляется посредством дополнительных операций в группе А. Нормализовать количество и размер объектов: Если количество объектов получилось слишком большим на ваш взгляд (на мой слишком много - ~10 и более) - сгруппировать связанные между собой объекты (объекты, которые используют операции друг друга). Если таких объектов нет, то стоит рассмотреть декомпозицию уже самой системы на несколько независимых на основании "здравого смысла" или более технических аспектов (по разработчикам, эксплуатационным требованиям, частоте релизов и т.п.). Если в одном объекте количество операций или ресурсов получилось слишком большим (~10 и ~4 и более соответственно), то надо рассмотреть возможность разбить этот объект на несколько более мелких, взаимодействующих через обмен сообщениями. Если такой возможности нет, то хотя бы выделить ресурсы во внутренние объекты (подпакеты) Первый шаг этой методики - определение операций и ресурсов - я описал в "посте с описанием построения диаграммы Эффектов проекта True Story Project". Пример, рассмотренный в этом посте, хорошо демонстрирует работу с внешними системами, но, в силу специфики исходного проекта, практически не касается вопроса декомпозиции на ресурсы собственного состояния системы. В общих чертах этот вопрос у меня раскрыт в посте про агрегаты, а пост с конкретным примером проектирования системы с развесистым собственным состоянием - в планах. Второй и третий же шаги я опишу в следующем посте, для которого данный является прелюдией с обоснованием необходимости создания собственной методики. Эта методика относительно простая и механистическая, но даёт на удивление хорошие результаты. Очевидно, что полученная декомпозиция обладает высокой степенью сокрытия информации - детали реализации (ресурсы) операций системы скрываются внутри объектов дизайна. Такая степень сокрытия информации является прочным фундаментом и для сведения сцепленности к минимуму. Вместе с низкой сцепленностью, рука об руку идёт и высокая связанность (количество зависимостей внутри пакета), которая подтверждается локальностью изменений в проектах, декомпозированных таким образом. Наконец, масштабирование также учтено и встроено в саму методику. С критериями оценки самой методики тоже всё хорошо. Объяснить её, конечно, сложнее, чем слоёную декомпозицию, но намного проще, чем остальные методики из группы декомпозиций на основе предметной области. То же касается и применения - проектирование объектов находится посередине между предельно простым проектированием слоёв и очень сложным проектированием фич, компонентов и ограниченных контекстов. Итоговая оценка пакетирования по объектам: Сокрытие информации - 5 (отл.) Сцепленность - 4 (хор.) Связанность - 4 (хор.) Масштабируемость - 5 (отл.) Простота объяснения - 3 (удв.) Простота применения - 3 (удв.) Все распространённые методики группировки классов по пакетам обладают существенными недостатками. Группировка по слоям даёт откровенно плохие результаты. Группировку по фичам и компонентам непонятно, как выполнять и где научиться. Группировку по ограниченным контекстам и агрегатам сложно изучить, а потом выполнить. Для того чтобы решить эти проблемы, я разработал методику объектно-ориентированной декомпозиции системы на пакеты. Она проще в изучении и применении группировок по фичам, компонентам и ограниченным контекстам/агрегатам, но даёт результаты такого же качества. В следующем посте я вернусь к серии о диаграмме эффектов и подробно рассмотрю процесс выполнения объектно-ориентированной декомпозиции на конкретном примере. Картинка кликабельна   Подход Сокр. инфы Сцепл-ть Связ-ть Масш-ть Обучение Применение Пакетирование по слоям 2 2 2 2 5 5 Пакетирование по фичам 4 4 4 3 2 2 Пакетирование по компонентам 3 3 4 3 2+ 2 Пакетирование по ограниченным контекстам и агрегатам 4 4 5 5 2 1 Пакетирование по объектам 5 4 4 5 3 3  Автор Эргономичного подхода, Kotlin/Backend техлид