• Всем привет! На связи Евгений и Сергей — авторы проекта StringConcat. На двоих у нас уже больше 30 лет опыта в разработке. В этой статье коротко расскажем о себе, о том, что мы делаем, зачем и кому это нужно.

    Два слова о нас

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

    Но, как оказалось, это было примерно как купить биткоин в 2010-м. Айтишечка поперла, а мы — вместе с ней.

    Мы работали, меняли проекты, успели побывать обычными разработчиками, а потом доросли до руководителей и технических директоров. Были в телекоме, финтехе и медицине. Разрабатывали десктопные приложения, софт для железок и серверные системы. Писали на PHP (ужас), Python (ужас-ужас), C, C++ и еще куче языков, пока в итоге не осели в JVM-болоте энтерпрайзных монстров, где и квакаем до сих пор.


    Как все начиналось

    Но самое удивительное — сколько бы мы ни ходили по проектам, проблемы везде были абсолютно одинаковые. Думаю, вы и себя в этом списке узнаете:

    • Никто уже не понимает сложную бизнес-логику и спрятался в своем маленьком огородике
    • При починке одного бага возникает еще три новых (хорошо если вообще что- запускается)
    • Мониторинг есть только в формате «недовольные клиенты обрывают телефон техподдержки»
    • Как устроена система целиком, знают только пара старцев, которым уже пора на пенсию (у одного из них старческое слабоумие)
    • Релиз и деплой — это микроинсульт у тимлида
    • В очередной раз сделали совсем не то, что было нужно

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

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

    Именно этот опыт и стал отправной точкой.

    В какой-то момент мы поймали себя на мысли: а что, если собрать все, что мы считаем важным, в единый, последовательный набор принципов? Такой себе путеводитель по разработке сложных систем. Сначала это было нужно только нам самим. Потом мы поняли, что это нужно и другим. Попробовали вынести наши подходы за пределы своих команд — и неожиданно оказалось, что запрос есть, причем вполне понятный. Они устали постоянно разгребать последствия, хотят делать свою работу более осознанно и оставаться востребованными специалистами.

    Кстати, мы всю дорогу рассказывали, что разработка — это процентов 20 всего процесса. Остальное — понимание бизнеса, предметной области, архитектуры, требований, компромиссов и всего того, что большинство старается скипнуть (я тут код пришел писать, а не вот это всё). И знаете, приятно иногда оказаться правыми. Пришел ИИ, навыки кодинга неплохо так подешевели, а вся остальная часть профессии никуда не делась (про ИИ как нибудь поговорим отдельно, так что подписывайтесь).

    Так появился проект, который в итоге мы назвали StringConcat. Когда-то мы шутили, что главное в нашей работе — правильно склеивать строки в браузере.

    Всем привет! На связи Евгений и Сергей — авторы проекта StringConcat. На двоих у нас уже больше 30 лет опыта в разработке. В этой статье коротко расскажем о себе, о том, что мы делаем, зачем и кому это нужно.

    Два слова о нас

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

    Но, как оказалось, это было примерно как купить биткоин в 2010-м. Айтишечка поперла, а мы — вместе с ней.

    Мы работали, меняли проекты, успели побывать обычными разработчиками, а потом доросли до руководителей и технических директоров. Были в телекоме, финтехе и медицине. Разрабатывали десктопные приложения, софт для железок и серверные системы. Писали на PHP (ужас), Python (ужас-ужас), C, C++ и еще куче языков, пока в итоге не осели в JVM-болоте энтерпрайзных монстров, где и квакаем до сих пор.


    Как все начиналось

    Но самое удивительное — сколько бы мы ни ходили по проектам, проблемы везде были абсолютно одинаковые. Думаю, вы и себя в этом списке узнаете:

    • Никто уже не понимает сложную бизнес-логику и спрятался в своем маленьком огородике
    • При починке одного бага возникает еще три новых (хорошо если вообще что- запускается)
    • Мониторинг есть только в формате «недовольные клиенты обрывают телефон техподдержки»
    • Как устроена система целиком, знают только пара старцев, которым уже пора на пенсию (у одного из них старческое слабоумие)
    • Релиз и деплой — это микроинсульт у тимлида
    • В очередной раз сделали совсем не то, что было нужно

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

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

    Именно этот опыт и стал отправной точкой.

    В какой-то момент мы поймали себя на мысли: а что, если собрать все, что мы считаем важным, в единый, последовательный набор принципов? Такой себе путеводитель по разработке сложных систем. Сначала это было нужно только нам самим. Потом мы поняли, что это нужно и другим. Попробовали вынести наши подходы за пределы своих команд — и неожиданно оказалось, что запрос есть, причем вполне понятный. Они устали постоянно разгребать последствия, хотят делать свою работу более осознанно и оставаться востребованными специалистами.

    Кстати, мы всю дорогу рассказывали, что разработка — это процентов 20 всего процесса. Остальное — понимание бизнеса, предметной области, архитектуры, требований, компромиссов и всего того, что большинство старается скипнуть (я тут код пришел писать, а не вот это всё). И знаете, приятно иногда оказаться правыми. Пришел ИИ, навыки кодинга неплохо так подешевели, а вся остальная часть профессии никуда не делась (про ИИ как нибудь поговорим отдельно, так что подписывайтесь).

    Так появился проект, который в итоге мы назвали StringConcat. Когда-то мы шутили, что главное в нашей работе — правильно склеивать строки в браузере.

  • Этот пост — текстовая версия роликов по Value Object (раз и два). Здесь мы разбираем не столько теорию, сколько реальные примеры из проектов. DDD — не самая простая область, поэтому хочется показать не только удачные решения, но и грабли, на которые мы сами наступали. Код из примеров лежит в репозитории, ссылка на него есть в конце статьи.

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


    Определение

    Value Object (объект-значение) — концепция из Domain-Driven Design для моделирования объектов, которые не имеют собственной идентичности и определяются исключительно своими свойствами. Их равенство определяется значениями полей, а не ссылкой или идентификатором — мы сравниваем только содержимое. Это могут быть, например, денежные суммы, координаты, даты, цвета, адреса и т. д.

    У Value Object есть несколько основных свойств:

    • Неизменяемость. Не во всех языках объект можно сделать неизменяемым, но предполагается, что после создания Value Object мы не меняем его состояние. Если нужно что-то изменить, создаём новый экземпляр.
    • Сравнение по значению. Равенство объектов определяется значениями их полей.
    • Отсутствие побочных эффектов. По сути, реализация работает как функция без побочных эффектов: результат зависит только от входных параметров.

    Самый простой пример: Email

    data class Email internal constructor(private val email: String) {
    
        init {
            check(isValid(email))
        }
    
        companion object {
            private val REGEX =
                Regex("^[a-zA-Z0-9_!#\$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+\$")
    
            fun from(email: String) =
                if (isValid(email)) {
                    Email(email).right()
                } else {
                    InvalidFormatError.left()
                }
    
            private fun isValid(email: String) = REGEX.matches(email)
        }
    }
    
    object InvalidFormatError
    

    Для создания экземпляра используется фабричный метод from, внутри которого выполняется валидация: мы проверяем, что e-mail валиден, а конструктор имеет модификатор доступа internal. Если ввод валиден, возвращаем e-mail, если нет — InvalidFormatError.

    Такая конструкция (.left() / .right()) взята из библиотеки Arrow. Если коротко, это способ обозначить ошибку без исключений и самодельных кодов возврата: метод возвращает объект Either. Если вернулась левая часть — значит, произошла ошибка, если правая — мы получили сам e-mail.

    Ничего сверхъестественного здесь нет: вместо Either можно использовать исключения, если такой подход вам привычнее.


    Что даёт паттерн?

    Пройдёмся от самого очевидного к менее очевидному.

    Этот пост — текстовая версия роликов по Value Object (раз и два). Здесь мы разбираем не столько теорию, сколько реальные примеры из проектов. DDD — не самая простая область, поэтому хочется показать не только удачные решения, но и грабли, на которые мы сами наступали. Код из примеров лежит в репозитории, ссылка на него есть в конце статьи.

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


    Определение

    Value Object (объект-значение) — концепция из Domain-Driven Design для моделирования объектов, которые не имеют собственной идентичности и определяются исключительно своими свойствами. Их равенство определяется значениями полей, а не ссылкой или идентификатором — мы сравниваем только содержимое. Это могут быть, например, денежные суммы, координаты, даты, цвета, адреса и т. д.

    У Value Object есть несколько основных свойств:

    • Неизменяемость. Не во всех языках объект можно сделать неизменяемым, но предполагается, что после создания Value Object мы не меняем его состояние. Если нужно что-то изменить, создаём новый экземпляр.
    • Сравнение по значению. Равенство объектов определяется значениями их полей.
    • Отсутствие побочных эффектов. По сути, реализация работает как функция без побочных эффектов: результат зависит только от входных параметров.

    Самый простой пример: Email

    data class Email internal constructor(private val email: String) {
    
        init {
            check(isValid(email))
        }
    
        companion object {
            private val REGEX =
                Regex("^[a-zA-Z0-9_!#\$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+\$")
    
            fun from(email: String) =
                if (isValid(email)) {
                    Email(email).right()
                } else {
                    InvalidFormatError.left()
                }
    
            private fun isValid(email: String) = REGEX.matches(email)
        }
    }
    
    object InvalidFormatError
    

    Для создания экземпляра используется фабричный метод from, внутри которого выполняется валидация: мы проверяем, что e-mail валиден, а конструктор имеет модификатор доступа internal. Если ввод валиден, возвращаем e-mail, если нет — InvalidFormatError.

    Такая конструкция (.left() / .right()) взята из библиотеки Arrow. Если коротко, это способ обозначить ошибку без исключений и самодельных кодов возврата: метод возвращает объект Either. Если вернулась левая часть — значит, произошла ошибка, если правая — мы получили сам e-mail.

    Ничего сверхъестественного здесь нет: вместо Either можно использовать исключения, если такой подход вам привычнее.


    Что даёт паттерн?

    Пройдёмся от самого очевидного к менее очевидному.

  • DDD в машинном обучении: опыт реального проекта
    Подпишитесь на уровень «Начинающий душнила»Уже есть подписка?
    Notebook → прод, метод на 1000 строк, цикл в цикле в цикле, никто не понимает что происходит - это классика от MLщиков. Мы применили DDD и чистую архитектуру к реальному ML-проекту. Value Objects, агрегаты, доменные сервисы, порты — на конкретных примерах кода.Подпишитесь, чтобы читать далее
    Начинающий душнила
  • Начинающий душнила