Может ли магистратура сделать из разработчика мидл-специалиста
Можем ли мы говорить, что после онлайн-магистратуры студенты станут мидлами? Конечно, обещать такое мы не можем. Даём ли мы знания, которым должен обладать мидл-специалист? Да, даём. Но очень многое зависит от самого студента: его мотивации, желания выделять время на учёбу и проекты. Мидл — это не только про формально пройденный материал, это ещё и про опыт и уровень самостоятельности специалиста.
Может ли студент закончивший медицинскую академию сразу идти оперировать людей? Может ли студент закончивший строительный ВУЗ сразу пойти проектировать дома? А если курсы специальные пройти? Думаю ответ очевиден. А программист конечно же сразу станет мидлом если ему секретные знания расскажут?
Для того чтобы стать условным мидлом, нужно уже быть джуном хотя бы год-полтора. Уже иметь устоявшиеся джуновские знания и опыт, уже набить все необходимые шишки. Чтобы двигаться на новый уровень нужен фундамент старого.
Все рассказы про то что можно сразу перепрыгнуть позицию не более чем реклама курсов.
А в Java при добавлении generic-ов решили не ломать совместимость и не плодить коллекций, а раз уж всё равно боксить, то нафиг value types?
Не совсем так. Они пробовали сделать поддержку специализированных дженериков но не смогли достичь ощутимых результатов, поэтому остановились на боксинге. На эту тему есть целая статья Одерски как они это делали.
просто размышляю о том, почему некоторые выгодные во всех смыслах решения в свое время не были переосмыслены и скопированы
Потому что это решение не выгодно именно "во всех смыслах". У Java и C# разная философия, нет большого смысла из Java делать второй C#.
Java проще на начинающих, в ней сильно сложнее отстрелить себе ноги - это ее основная философия с самого начала. Если бы добавили struct то язык стал бы сильно сложнее, а потом вместе in, out, ref, span и понеслось. Плюс, есть многие вещи в JVM которые принципиально плохо совместимы с этим. На вскидку не скажу но там и на GC завязано и в целом на всю спецификацию.
Но основной посыл что не было смысла делать клон C# с большим количеством детских болезней.
Да, помню я эти времена, когда особой популярностью пользовались ассемблерные вставки ровно под тот же речитатив про "контроль, я могу управлять, мне виднее" и все такое. И как потом полыхало так, что зарево было видно за километры, когда оказалось, что компилятор давным-давно все это делает куда лучше и оптимальнее. Так и тут - это не контроль, а видимость контроля, который в среднем по больнице никогда не будет оправдан. Так что лично я могу только приветствовать, что джава не грузит меня этим самым контролем, а тихонько под капотом делает свое дело.
Вопрос исключительно - для чего.
Java/JVM сейчас это на 99% backend приложения и заточена на среднего специалиста, с философией - разработчику доверять нельзя, он может отстрелить себе ноги, поэтому все запретим и из кожи вон будем вылезать компенсируя свои запреты, чтобы закрыть проблемы производительности, памяти и безопасности пассивно. Для backend приложений - это более чем оправдано.
C#/.Net это инструмент практически для любых задач и вообще другая история вида - мы все взрослые люди, мы можем писать и на высоком уровне но у нас в кладовке есть все инструменты для специалистов с прямыми руками и мы не боимся ими пользоваться
Все эти разговоры про:
компилятор давным-давно все это делает куда лучше и оптимальнее
это не контроль, а видимость контроля, который в среднем по больнице никогда не будет оправдан
они от избалованности написанием современных приложений где можно класть на производительность и расход памяти. Как только появляются какие-то требования к производительности никто в здравом уме не будет полагаться на blackbox, что-там на уме у компилятора в этом релизе, на каком тире что-то будет оптимизировано и молиться на то чтобы все само оптимизировалось.
Как только struct присваивается переменной интерфейсного типа, он боксится — ведь интерфейс ссылочный. Вызов s.Area() идёт уже по упакованной копии. Если вы держите Circle в локальной переменной конкретного типа и вызываете Area() напрямую — боксинга нет. Грабли вылезают, когда struct “прячут” за интерфейс ради полиморфизма.
Лечение: generic-ограничение where T : struct, IShape вместо переменной интерфейса — JIT специализирует код по value-типу и вызывает метод без упаковки.
Ссылочность вообще не причем. Структуры прекрасно передаются по ссылке без всяких аллокаций. Проблема в том что каждая передаваемая переменная должна иметь фиксированный заранее известный размер. Если переменная интерфейсного типа то размер фактически передаваемых данных может быть любым до 0 и далее. Не может быть переменной или параметра, размер которого никто не знает при исполнении. В отличии от ссылки размер которой всегда фиксированный. Боксинг это способ впихнуть невпихуемое.
Когда мы пишем ограничение where T : struct, IShape то это совсем другая история, так как это скрывает вызов отдельной предварительно сгенерированной функции для каждого используемого типа. У каждой функции будет свой параметр фиксированного размера.
Боксинг, который уже НЕ боксит: чему научился рантайм
Generic-методы со struct-аргументом не боксят сам аргумент.EqualityComparer<T>.Default, List<T>, Dictionary<TKey,TValue> специализируются по value-типу: JIT генерирует отдельный нативный код на каждый struct-аргумент, и упаковки value→object там нет (при условии корректного IEquatable<T> — см. пункт 3).
Что значит уже? В C# изначально мономорфизация параметров типов что значит что любой вариант типа с параметром мономорфизируеться в отдельный тип, там нечему бокситься и незачем. Другими словами, не было никакого List<int> всегда был List_int.
Баян, конечно. Но раз спрашивают везде, давайте по-честному — и без карикатур на джуна.
🟢 Джун обычно отвечает уверенно и правильно: equals — это равенство по значению, == — по ссылке; переопределил equals — переопредели и hashCode, это контракт; равные объекты обязаны вернуть равный hashCode. Генерю через IDE, Lombok или беру record. Нормальный ответ, он правда так знает.
🟡 Мидл показывает, что будет, если контракт нарушить. Переопределили только equals — и два «равных» объекта дадут разный hashCode, улетят в разные бакеты HashMap, а get() по второму ключу вернёт null, хотя элемент в карте лежит. Обратное контрактом не требуется: одинаковый хэш у разных объектов это просто коллизия, и это нормально. Сюда же правило, которое экономит нервы: hashCode нельзя строить на полях, которые потом меняются.
🔴 Сеньор говорит не про новые факты, а про то, как это спроектировать, чтобы не рвануло в реальном приложении. JPA-сущности: id генерит база, до сохранения он null, и наивный equals по id ведёт detached- и managed-объекты по-разному — Set начинает «терять» элементы между сессиями. Лечится бизнес-ключом или фиксированным hashCode. Плюс ловушка с Hibernate: ленивый прокси подменяет класс объекта, поэтому equals через getClass() ломается на прокси, и берут instanceof. Вот эта прод-боль и отличает сеньора, а не знание самого контракта.
Нормальный сеньор дает на простой вопрос - простой ответ. Если я спросил про equals и hashCode а кандидата начинает нести куда то в JPA и Hibernate хотя я даже не спрашивал его про это, значит что он мне зубы заговаривает а это на любом собесе или экзамене признак неуверенности.
К сожалению, такие слова про простоту проекта без репозиторием, сервисов, оберток и т.д. остаются голословными утверждениями. Многие говорят, что это всё не нужно, но ни разу не видел крупный (практически production-ready) проект, написанный таким образом и который бы можно было ставить в пример вариантам с усложнениями и говорить: "Вот пример простого и крупного проекта".
Другими словами: "докажите что не нужно". Но ведь Ваше же утверждение можно применить и в обратную сторону - "докажите что нужно".
Многие говорят, что это всё нужно, но ни разу не видел крупный (практически production-ready) проект, написанный таким образом и который бы:
не становился мага тяжелым, сложным и запутанным за счет огромного числа абстракций на всякий случай
не имел протекающие абстракции на каждом шагу
не имел огромного числа костылей, потому что реальная жизнь не натягивается на чистые архитектуры
Более того, в подавляющем числе статей и книг нельзя посмотреть как реализуются на практике советы по сложным архитектурам.
Найти и предоставить хороший простой проект с простой архитектурой не проще чем найти такой же хороший со сложной.
Производительность часто приносится в жертву академической чистоте.
С точки зрения строгой архитектурной чистоты здесь возникает асимметрия: для записи у нас остаются доменные сущности, сервисы, репозитории и транзакции, а для чтения появляется отдельный optimized read‑path. Это уже не выглядит так «красиво», как каноническая layered‑схема, но в production такая асимметрия часто оказывается оправданной.
Огромное заблуждение считать что есть какая-то архитектурная чистота и каноничность. Все эти чистые архитектуры и DDD это не более чем завирусившиеся авторские методики отдельных скучающих писателей. Будучи объективным нужно сказать что нет никаких хотя бы околонаучных доказательств что вся эта многослойность, вся эта эмуляция реального мира, вся эта чисто-архитектурность дает пользы больше чем вреда. Да по папочкам разложено, да уложено приложено, все реализации поменять можно, вот только там где в нормальном проекте для доработки хватить пары строк, в книжных архитектурах все будет когнитивно переусложнено, раскидано по десяткам классов и будут уходить часы, дни и недели на обсуждения архитектурной частоты согласно книжке и будет написаны тысячи строк кода которые решают проблемы которая такая архитектура и создала и не имеют никакой практической ценности для практической задачи.
Это я к чему. Наверное не стоит делать такие противопоставления. Делать из перехайпленых книжных подходов какой то эталон. Под задачу выбираем инструменты а не под инструменты подгоняем задачу.
Вообще, это забавно и печально одновременно - как в одно идиотское политическое решение cделать .Net только под Windows, до сих пор не дает, технически куда более зрелому и совершенному .Net(C#) обойти Java(JVM) исключительно по политическим причинам. Один раз упустили, а потом все по привычке будут делать все только на Java.
Второй забавный момент - изначально именно Java/JVM делалась как инструмент под все платформы-задачи а сейчас все наоборот, C#/CLR - хороший инструмент для решения практически любой задачи, в то время как Java/JVM в подавляющей доле это инструмент написания корпоративного backend и практически ничего более.
Зачем называть вещи своими именами если можно заменить слово "программист" на "инженер" и все вдруг станет загадочнее, элитарнее, понтовее и круче. И пофиг что чем дальше тем средний программист тупее в техническом плане с каждым годом, главное что теперь он гордо зовется "инженер" и занимается они не никчемным программированием а целой инженерией!
Толку от переноса такой локальной логики - мало. Если проверка подписания - это проверка сущности и проверка в БД то мне ни горячо ни холодно от того что какой то кусок логики лежит в сущности. Мне все-равно нужно искать способы шарить логику связанную с походами в БД. А потом когда я зашарю логику usecase мне нужно дружить с отдельно зашареной логикой из сущности. Вопрос зачем мне все это если я просто могу написать метод который в себе будет содержать вообще все что для этого нужно?
Логика в сущности - это бесполезная полу-мера. Никакого толку от метода is_signed в сущности нет, потому что по итогу она мне не отвечает на вопрос - подписано ли что-то или нет, мне нужно еще искать другую половинку логики. Проще все концентрировать в одном месте где таких ограничений нет.
PS. Потом будет еще веселей, когда захотите реагировать на события. Будете городить городьбу внутри сущности, возвращать из нее события, пришивать эти события к юзкейсам и прочий мрак. Знаем проходили.
Сервисный слой(use cases) в любом случае остаётся, вопрос тут скорее не "сервис или нет", а в том, где живут бизнес операции, относящиеся к конкретной сущности.Если такая логика находится в сервисах, то при росте системы мы получаем дублирование правил или вынуждены выносить их в хелперы, что увеличивает связность.
Не получаем. Все бизнес-операции сущности живут в сервисе сущности и больше нигде.
Если логика проверки "уже подписывал или нет" находится в нескольких сервисах, нам придётся менять её во всех этих местах.Если же локальные бизнес-операции сущности инкапсулированы внутри сущности (например, invoice.add_signer() или invoice.is_signed_by(user)), то изменение set -> list затрагивает только саму сущность, а внешний код остаётся стабильным
Как я уже написал - логика не будет в нескольких сервисах.
В Вашем примере данная логика работает ровно до той поры пока там нет работы ввода вывода. Если логика проверки подписи требует сходить в другую систему, или хотя бы в БД, то метод сущности превращается в тыкву и все нужно переделывать и решать вопрос с перед-использованием. Вариант с управляющим сервисом такой проблемы лишен.
Domain model в моём примере не пытается инкапсулировать вообще весь бизнес процесс системы.
Не понял почему "не самодостаточна даже для соблюдения своих собственных инвариантов", можете подробнее раскрыть, пожалуйста.
Ну смотрите, давайте от обратного - зачем нам вообще какие-либо бизнес -правила описывать в модели? Вполне разумные преимущества: 1.концентрируем логику в одном месте - из какого бы участка кода мы не вызвали сущность, нельзя привести ее в неправильное состояние 2.вместо изменения отдельных полей мы делаем осмысленные операции с валидацией состояний и прочим Звучит здорово, но ломается в то что это полумера. Сущность неполноценна. Ну допустим, мы зашили в ней правила касаемо ее полей. А какой в этом толк? Вот есть операция - подписать. В ней мы меняем поля signature, signer, signed_at, предварительно проверил состояние подписываемого объекта. красиво. А потом в проверку приходиться вставлять запрос и толк от этой схемы стремиться к нулю, потому что из любого места в коде можно сделать подпись и сохранить а внешние проверки застряли в юзкейсе. При этом мы разнесли логику по разным классам, ходи потом бегай и собирай по кускам как делаться подпись. Решительных преимуществ не получили а вот недостатки в полной мере.
Теперь сравните с классической схемой когда все это будет написано в сервисе. В сервисе будет представлена вся логика работы с доменом от и до и без ограничений. Вы нажимаете метод sign и в нем вся бизнес логика, все инварианты и проверки, все походы в БД и во все интеграции. Все сконцентрировано в одном месте. Если сущность будет глупой но Вы просто запретите менять ее поля в любом месте кроме ее сервиса то получите концентрацию логики в одном месте без всех этих проблем и костылей.
Мы хотим, чтобы собеседование было диалогом про инженерное мышление и опыт.
Что мы для этого делаем?
2/5 технических встреч, остальные это гуманитарное балабольство на тему ценностей, тердностей, самых сложных задач, философии принятия решений и видимо вопросов в духе - кем Вы себя видите через 5 лет?
Не очень понимаю к чему все эти заявления про инженерное мышление, если описание собеседования - это просто классический пример современного, модного, бессмысленного и беспощадного подхода как у всех.
Доменная модель описывает бизнес-сущность и её правила.
Обратите внимание, домен ничего не знает про инфраструктуру, здесь нет ORM, SQL, pydantic, boto3, etc.
Раз, и между двумя предложениями рождается противотечение. Это фундаментальная проблема ООП подхода на бэке. Мы не можем оперировать всей доменной логикой без инфраструктуры. Как только появиться необходимость валидировать на существование других данных в БД или что еще хуже, менять их - этот подход сразу превратиться в тыкву.
У нас получилась модель которая не самодостаточна даже для соблюдения своих собственных инвариантов а значит толку от нее ноль, она все равно нормально не работает и намного удобнее сконцентрировать всю доменную логику в одном месте - в сервисе.
Окей. Зачем нам этот класс? Вы по сути написали очень странную и неуклюжую функцию. Просто передайте storage и uow в параметры и выкинете никому не нужный класс.
Во-вторых, у вас в этом классе, как я и писал раньше, торчат инварианты инварианты, которые размазаны между несколькими классами.
В-третих, что будете делать, когда появиться общая логика между usecases? Дублировать? Или выносить в еще одну костыль-абстракцию?
Сервис намного проще как решение. Ну или просто оставить это функциями.
И, возможно, именно в эпоху AI эта стоимость начинает постепенно снижаться. То, что раньше требовало большого количества рутинной работы, всё чаще генерируется, поддерживается и рефакторится значительно проще.
Ну тут вопрос то от обратного - зачем AI вся эта ерунда? Для него нет никакой проблемы разобраться и распутать или вообще переписать и как раз наоборот, лучше отдать предпочтение очень простому коду, который AI может зарефачить там где раньше это была боль. Для человека простой минималистичный код куда проще и для понимания и для валидации AI чем ломать мозг от валидации тонны кода при добавлении одного флага.
Уточню на всякий случай, речь идет о проектах связанных с ML и подобным или вообще о всех существующих типах проектов?
Я правильно понимаю, с Ваших слов,что приложение на python можно написать быстрее чем на Go в 10 раз?
Может ли студент закончивший медицинскую академию сразу идти оперировать людей?
Может ли студент закончивший строительный ВУЗ сразу пойти проектировать дома?
А если курсы специальные пройти?
Думаю ответ очевиден.
А программист конечно же сразу станет мидлом если ему секретные знания расскажут?
Для того чтобы стать условным мидлом, нужно уже быть джуном хотя бы год-полтора. Уже иметь устоявшиеся джуновские знания и опыт, уже набить все необходимые шишки. Чтобы двигаться на новый уровень нужен фундамент старого.
Все рассказы про то что можно сразу перепрыгнуть позицию не более чем реклама курсов.
Не совсем так. Они пробовали сделать поддержку специализированных дженериков но не смогли достичь ощутимых результатов, поэтому остановились на боксинге. На эту тему есть целая статья Одерски как они это делали.
Потому что это решение не выгодно именно "во всех смыслах". У Java и C# разная философия, нет большого смысла из Java делать второй C#.
Java проще на начинающих, в ней сильно сложнее отстрелить себе ноги - это ее основная философия с самого начала. Если бы добавили struct то язык стал бы сильно сложнее, а потом вместе in, out, ref, span и понеслось. Плюс, есть многие вещи в JVM которые принципиально плохо совместимы с этим. На вскидку не скажу но там и на GC завязано и в целом на всю спецификацию.
Но основной посыл что не было смысла делать клон C# с большим количеством детских болезней.
Вопрос исключительно - для чего.
Java/JVM сейчас это на 99% backend приложения и заточена на среднего специалиста, с философией - разработчику доверять нельзя, он может отстрелить себе ноги, поэтому все запретим и из кожи вон будем вылезать компенсируя свои запреты, чтобы закрыть проблемы производительности, памяти и безопасности пассивно. Для backend приложений - это более чем оправдано.
C#/.Net это инструмент практически для любых задач и вообще другая история вида - мы все взрослые люди, мы можем писать и на высоком уровне но у нас в кладовке есть все инструменты для специалистов с прямыми руками и мы не боимся ими пользоваться
Все эти разговоры про:
они от избалованности написанием современных приложений где можно класть на производительность и расход памяти. Как только появляются какие-то требования к производительности никто в здравом уме не будет полагаться на blackbox, что-там на уме у компилятора в этом релизе, на каком тире что-то будет оптимизировано и молиться на то чтобы все само оптимизировалось.
Каждый инструмент под свои задачи.
Ссылочность вообще не причем. Структуры прекрасно передаются по ссылке без всяких аллокаций. Проблема в том что каждая передаваемая переменная должна иметь фиксированный заранее известный размер. Если переменная интерфейсного типа то размер фактически передаваемых данных может быть любым до 0 и далее. Не может быть переменной или параметра, размер которого никто не знает при исполнении. В отличии от ссылки размер которой всегда фиксированный. Боксинг это способ впихнуть невпихуемое.
Когда мы пишем ограничение
where T : struct, IShapeто это совсем другая история, так как это скрывает вызов отдельной предварительно сгенерированной функции для каждого используемого типа. У каждой функции будет свой параметр фиксированного размера.Что значит уже? В C# изначально мономорфизация параметров типов что значит что любой вариант типа с параметром мономорфизируеться в отдельный тип, там нечему бокситься и незачем. Другими словами, не было никакого
List<int>всегда был List_int.Нормальный сеньор дает на простой вопрос - простой ответ. Если я спросил про equals и hashCode а кандидата начинает нести куда то в JPA и Hibernate хотя я даже не спрашивал его про это, значит что он мне зубы заговаривает а это на любом собесе или экзамене признак неуверенности.
Другими словами: "докажите что не нужно". Но ведь Ваше же утверждение можно применить и в обратную сторону - "докажите что нужно".
Многие говорят, что это всё нужно, но ни разу не видел крупный (практически production-ready) проект, написанный таким образом и который бы:
не становился мага тяжелым, сложным и запутанным за счет огромного числа абстракций на всякий случай
не имел протекающие абстракции на каждом шагу
не имел огромного числа костылей, потому что реальная жизнь не натягивается на чистые архитектуры
Более того, в подавляющем числе статей и книг нельзя посмотреть как реализуются на практике советы по сложным архитектурам.
Найти и предоставить хороший простой проект с простой архитектурой не проще чем найти такой же хороший со сложной.
Огромное заблуждение считать что есть какая-то архитектурная чистота и каноничность. Все эти чистые архитектуры и DDD это не более чем завирусившиеся авторские методики отдельных скучающих писателей. Будучи объективным нужно сказать что нет никаких хотя бы околонаучных доказательств что вся эта многослойность, вся эта эмуляция реального мира, вся эта чисто-архитектурность дает пользы больше чем вреда. Да по папочкам разложено, да уложено приложено, все реализации поменять можно, вот только там где в нормальном проекте для доработки хватить пары строк, в книжных архитектурах все будет когнитивно переусложнено, раскидано по десяткам классов и будут уходить часы, дни и недели на обсуждения архитектурной частоты согласно книжке и будет написаны тысячи строк кода которые решают проблемы которая такая архитектура и создала и не имеют никакой практической ценности для практической задачи.
Это я к чему. Наверное не стоит делать такие противопоставления. Делать из перехайпленых книжных подходов какой то эталон. Под задачу выбираем инструменты а не под инструменты подгоняем задачу.
Вообще, это забавно и печально одновременно - как в одно идиотское политическое решение cделать .Net только под Windows, до сих пор не дает, технически куда более зрелому и совершенному .Net(C#) обойти Java(JVM) исключительно по политическим причинам. Один раз упустили, а потом все по привычке будут делать все только на Java.
Второй забавный момент - изначально именно Java/JVM делалась как инструмент под все платформы-задачи а сейчас все наоборот, C#/CLR - хороший инструмент для решения практически любой задачи, в то время как Java/JVM в подавляющей доле это инструмент написания корпоративного backend и практически ничего более.
Хвост начинает вилять собакой
Просто иронизирую на тему компенсации профессиональных качеств красивыми названиями
Кто бы тогда мог подумать что сейчас это будет +1 к цветовой дифференциации штанов
Зачем называть вещи своими именами если можно заменить слово "программист" на "инженер" и все вдруг станет загадочнее, элитарнее, понтовее и круче. И пофиг что чем дальше тем средний программист тупее в техническом плане с каждым годом, главное что теперь он гордо зовется "инженер" и занимается они не никчемным программированием а целой инженерией!
Толку от переноса такой локальной логики - мало. Если проверка подписания - это проверка сущности и проверка в БД то мне ни горячо ни холодно от того что какой то кусок логики лежит в сущности. Мне все-равно нужно искать способы шарить логику связанную с походами в БД. А потом когда я зашарю логику usecase мне нужно дружить с отдельно зашареной логикой из сущности. Вопрос зачем мне все это если я просто могу написать метод который в себе будет содержать вообще все что для этого нужно?
Логика в сущности - это бесполезная полу-мера. Никакого толку от метода is_signed в сущности нет, потому что по итогу она мне не отвечает на вопрос - подписано ли что-то или нет, мне нужно еще искать другую половинку логики. Проще все концентрировать в одном месте где таких ограничений нет.
PS. Потом будет еще веселей, когда захотите реагировать на события. Будете городить городьбу внутри сущности, возвращать из нее события, пришивать эти события к юзкейсам и прочий мрак. Знаем проходили.
Не получаем. Все бизнес-операции сущности живут в сервисе сущности и больше нигде.
Как я уже написал - логика не будет в нескольких сервисах.
В Вашем примере данная логика работает ровно до той поры пока там нет работы ввода вывода. Если логика проверки подписи требует сходить в другую систему, или хотя бы в БД, то метод сущности превращается в тыкву и все нужно переделывать и решать вопрос с перед-использованием. Вариант с управляющим сервисом такой проблемы лишен.
Ну смотрите, давайте от обратного - зачем нам вообще какие-либо бизнес -правила описывать в модели? Вполне разумные преимущества:
1.концентрируем логику в одном месте - из какого бы участка кода мы не вызвали сущность, нельзя привести ее в неправильное состояние
2.вместо изменения отдельных полей мы делаем осмысленные операции с валидацией состояний и прочим
Звучит здорово, но ломается в то что это полумера. Сущность неполноценна. Ну допустим, мы зашили в ней правила касаемо ее полей. А какой в этом толк?
Вот есть операция - подписать. В ней мы меняем поля signature, signer, signed_at, предварительно проверил состояние подписываемого объекта. красиво. А потом в проверку приходиться вставлять запрос и толк от этой схемы стремиться к нулю, потому что из любого места в коде можно сделать подпись и сохранить а внешние проверки застряли в юзкейсе. При этом мы разнесли логику по разным классам, ходи потом бегай и собирай по кускам как делаться подпись. Решительных преимуществ не получили а вот недостатки в полной мере.
Теперь сравните с классической схемой когда все это будет написано в сервисе. В сервисе будет представлена вся логика работы с доменом от и до и без ограничений. Вы нажимаете метод sign и в нем вся бизнес логика, все инварианты и проверки, все походы в БД и во все интеграции. Все сконцентрировано в одном месте. Если сущность будет глупой но Вы просто запретите менять ее поля в любом месте кроме ее сервиса то получите концентрацию логики в одном месте без всех этих проблем и костылей.
Что мы хотим?
Что мы для этого делаем?
2/5 технических встреч, остальные это гуманитарное балабольство на тему ценностей, тердностей, самых сложных задач, философии принятия решений и видимо вопросов в духе - кем Вы себя видите через 5 лет?
Не очень понимаю к чему все эти заявления про инженерное мышление, если описание собеседования - это просто классический пример современного, модного, бессмысленного и беспощадного подхода как у всех.
Немного покритикую что хорошо а что нет.
Раз, и между двумя предложениями рождается противотечение. Это фундаментальная проблема ООП подхода на бэке. Мы не можем оперировать всей доменной логикой без инфраструктуры. Как только появиться необходимость валидировать на существование других данных в БД или что еще хуже, менять их - этот подход сразу превратиться в тыкву.
У нас получилась модель которая не самодостаточна даже для соблюдения своих собственных инвариантов а значит толку от нее ноль, она все равно нормально не работает и намного удобнее сконцентрировать всю доменную логику в одном месте - в сервисе.
Дальше:
Окей. Зачем нам этот класс? Вы по сути написали очень странную и неуклюжую функцию. Просто передайте storage и uow в параметры и выкинете никому не нужный класс.
Во-вторых, у вас в этом классе, как я и писал раньше, торчат инварианты инварианты, которые размазаны между несколькими классами.
В-третих, что будете делать, когда появиться общая логика между usecases? Дублировать? Или выносить в еще одну костыль-абстракцию?
Сервис намного проще как решение. Ну или просто оставить это функциями.
Ну тут вопрос то от обратного - зачем AI вся эта ерунда? Для него нет никакой проблемы разобраться и распутать или вообще переписать и как раз наоборот, лучше отдать предпочтение очень простому коду, который AI может зарефачить там где раньше это была боль. Для человека простой минималистичный код куда проще и для понимания и для валидации AI чем ломать мозг от валидации тонны кода при добавлении одного флага.