Комментарии 172
Ответ на вопрос?)
Зачем писать на PHP в 2020?
Ответ на вопрос?)
Не думал об этом)
Я имел ввиду что о PHP говорено переговорено, на Reddit, на Quora, вроде выяснили что PHP нужен, а здесь снова старая песня о главном)
А ответ простой: мне лично ненра стремление писать большие проекты на go (да, меня бесит он я бы даже сказал, я хочу синтаксический сахар в полном объеме) да и наработок под него слишком много писать надо. А от явы многие почему то плюются. Собственно альтернативы закончились.
Собственно альтернативы закончились.
Ну почему же. Ещё C# есть и производные от JVM (котлин e.g.).
Полно: Python, Node, Java, .Net, а если считать по веб-фреймворкам, а не языкам, то ещё больше.
выложите потом конспект.
Привет, да, расшифровку хотим опубликовать — осталось, чтобы получилось контентно)
А от явы многие почему то плюются
Подобные заявления походят на бородатый анекдот: «Пастернака не читал, но осуждаю.»
У java довольно простой, лаконичный синтаксис и хорошо проработанный ООП, которым не каждый ЯП похвастается.
У java хорошая обратная совместимость версий (местами это даже смущает).
У java огромное сообщество, огромное кол-во библиотек и фреймворков.
У java самодокументируемый код, всегда есть возможность посмотреть не только документацию по методу, но и саму его реализацию с комментариями авторов.
За счет jit компиляции производительность «горячих» методов не сильно уступает нативному коду.
Безусловно у любого ЯП есть огрехи, но хотелось бы услышать конкретные аргументы нежели эмоциональные выпады.
У java довольно простой, лаконичный синтаксис и хорошо проработанный ООП, которым не каждый ЯП похвастается.Синтаксис даже слишком простой, что выливается в огромное количество бойлерплейта. И да, это огромная проблема, хотя её старательно пытаются не замечать.
ООП в java это по большей части overengineering, который не решает реальных проблем. При этом мутабильность спокойно ломает инкапсуляцию, но это, опять же, стараются не замечать.
У java самодокументируемый код, всегда есть возможность посмотреть не только документацию по методу, но и саму его реализацию с комментариями авторов.При этом слабая система типов, и нездоровая любовь к рефлексии, которые мешают писать надёжный код. Ну и NPE, да.
Синтаксис даже слишком простой, что выливается в огромное количество бойлерплейта.
Если вы про getter / setter, то Lombok решает эту проблему в 2 аннотации на классе (Getter / Setter). Нужен equals / hashCode – @EqualsAndHashCode. Нужен конструктор — @AllArgsConstructor. Нужен билдер — Builder, ну и т.д.
ООП в java это по большей части overengineering, который не решает реальных проблем
А подробнее можно о чем речь?
мутабильность спокойно ломает инкапсуляцию
Никто вам не запрещает писать безопасный код. Любой язык позволяет писать хорошо или плохо. Не хотите мутабельность — используйте immutable типы и final переменные.
нездоровая любовь к рефлексии, которые мешают писать надёжный код
Опять же, можно подробнее о чем речь?
Ну и NPE, да.
Для полей классов или аргументов методов можно поставить аннотацию @NonNull и все проверки будут сделаны за нас. Что касается цепочки вызовов, тут да, соглашусь, не хватает ?.
LombokОтличная вещь, но не везде он применим. В частности бывают проблемы с hibernate, и далеко не во всех проектах его используют. В legacy проектах его не встретишь.
А подробнее можно о чем речь?Об устройстве различных фреймворков, и о том, как пишутся enterprise приложения. По сути сплошные абстракции ради абстракций, которые в итоге текут.
Никто вам не запрещает писать безопасный код. Любой язык позволяет писать хорошо или плохо. Не хотите мутабельность — используйте immutable типы и final переменные.только в Java использовать иммутабельные структуры попросту неудобно. Например, как мне без мутаций поменять поле в цепочке объектов obj1.obj2.obj3.someField? К тому же библиотечный код часто не предполагает ссылочной прозрачности(например hibernate). Отдельного внимания заслуживают коллекции, интерфейс которых никак не может гарантировать иммутабельность. (А без lombok всё выглядит ещё печальнее)
Опять же, можно подробнее о чем речь?Например, использование аннотаций для валидации полей, вместо специальных типов. Мы могли бы сделать тип Email, а не использовать String с аннотацией. Или проверить статически что в массиве не меньше N элементов. Конечно, многие вещи можно так сделать(а некоторые не позволяет сделать система типов), но так просто не принято делать. Принято использовать аннотации, которые не могут гарантировать консистентность объекта в любой момент времени. Инъекция зависимостей, репозитарии Spring Data, маппинг Hibernate — все эти вещи можно было бы описать статически.
Вроде как раз из Java пришёл в PHP подход, используя который, мы создаём иммутабельный класс Email, в котором единственное обычно поле string помечается аннотацией для валидатора или просто в конструкторе императивно валидируется.
Нет, это не подход из Java. В Java, обычно, делают так:
https://www.baeldung.com/javax-validation
Я встречал что-то вроде
class User {
@NotNull
Email email;
}
class Email {
@NotNull
@Email
String value;
}
сначала в Java коде, а потом в PHP
Отличная вещь, но не везде он применим. В частности бывают проблемы с hibernat
А конкретнее в чем проблемы?
В legacy проектах его не встретишь.
Вся прелесть lombok, что его можно просто подключить и использовать. Свежий код пишем с его использованием, старый код постепенно рефакторим.
только в Java использовать иммутабельные структуры попросту неудобно. Например, как мне без мутаций поменять поле в цепочке объектов obj1.obj2.obj3.someField?
Соглашусь, неудобно. Можете показать пример технологии, где это сделать удобней?
Мы могли бы сделать тип Email, а не использовать String с аннотацией.
Так ведь никто не запрещает.
Или проверить статически что в массиве не меньше N элементов.
Вот только большая часть данных существует только в рантайме, потому статические проверки не будут иметь смысл.
Инъекция зависимостей, репозитарии Spring Data, маппинг Hibernate — все эти вещи можно было бы описать статически.
Так люди же намеренно пришли от статического к динамическому связыванию. Все реализации фабрик, сервисов, репозиториев можно и сейчас закостылить в лоб, вот только такой код не будет гибким для изменений.
Судя по критике — мутабельность, система типов, IoC — мне показалось, что вы больше приверженец ФП, я прав?
Бойлерплейт лично меня не волнует вообще, его чтение не напрягает, самая большая боль при поддержке — неочевидный код и башня аннотаций ну никак не добавляет очевидности.
А конкретнее в чем проблемы?У нас, в частности, используется обфускатор. Это боль и без Lombok. Я был бы очень рад воткнуть Lombok, но пока на это просто нет времени(а с полпинка не заводится). К тому же не все такие продвинутые, и во многих проектах просто запрещают его использовать(деды на null проверяли, и нам велели...).
Соглашусь, неудобно. Можете показать пример технологии, где это сделать удобней?
obj1 & obj2 . obj3 . someField .~ "someValue"
Вот только большая часть данных существует только в рантайме, потому статические проверки не будут иметь смысл.Будут. Статическая проверка позволяет гарантировать консистентность не только в момент валидации, но и на всём протяжении работы логики программы.
Так люди же намеренно пришли от статического к динамическому связыванию. Все реализации фабрик, сервисов, репозиториев можно и сейчас закостылить в лоб, вот только такой код не будет гибким для изменений.Кто говорит про «закостылить в лоб»? Есть и другие методы, кроме динамического связывания.
Судя по критике — мутабельность, система типов, IoC — мне показалось, что вы больше приверженец ФП, я прав?Я java программист, и конечно я большую часть времени использую ООП. От этого и критика. ФП только изучаю, но в целом уже сейчас понятны преимущества подхода ФП. Да и в целом размытое понятие ООП для меня теряет ценность. Непродуманность ООП можно обнаружить даже в классических определениях вроде «инкапсуляция, наследование, полиморфизм, (абстракция)». Ведь инкапсуляция присуща всем парадигмам, а наследование всего-лишь механизм конкретных языков. Остаётся полиморфизм подтипов?
У нас, в частности, используется обфускатор.
Знаю что для android разработки часто используют обфускацию, вы используете это и для enterprise web?
obj1 & obj2. obj3. someField .~ «someValue»
Выглядит как черная магия и призыв драконов. Понятно, что все дело в практике, но мне кажется синтаксис не должен быть слишком пугающим и магическим. Лучше сделать что-то явно, чем неявно.
ФП только изучаю, но в целом уже сейчас понятны преимущества подхода ФП
Увы я в ФП новичок, но поверхностного знакомства хватило понять, что система типов строится куда логичней и проще.
Но за кадром остается много других вещей. Насколько гибче получится сделать микросервис принимающий данные с клиента, обрабатывающий эти данные и сохраняющий их в БД?
Ну т.е. реализация на java: spring boot + data, описали модели, DTO, наставили аннотаций, описали репозитории, сервисы, контроллеры. Нужны новые трансформации данных — поправили сервисы, добавили контроллеры, все работает. Нужны изменения в структуре данных — добавили поле в модель, dto, добавили колонку в бд, все работает.
Насколько в haskel / erlang / lisp это сделать проще, гибче?
Знаю что для android разработки часто используют обфускацию, вы используете это и для enterprise web?Не я это придумал, и с этим часто бывают проблемы, но да — как-то так.
Выглядит как черная магия и призыв драконов. Понятно, что все дело в практике, но мне кажется синтаксис не должен быть слишком пугающим и магическим. Лучше сделать что-то явно, чем неявно.Да нет, вполне простой синтаксис. К тому же надо учитывать что это не отдельный специальный синтаксис, всё реализовано средствами языка. И неявного здесь ничего нет.
Но за кадром остается много других вещей. Насколько гибче получится сделать микросервис принимающий данные с клиента, обрабатывающий эти данные и сохраняющий их в БД?Если честно, я не так много сервисов делал на haskell, в основном пробовал разные библиотеки и подходы. Естественно приходилось всё изучать и сложно сравнивать по скорости разработки со знакомой мне java. Но саму логику писать довольно просто, для БД есть persistent, изменять структуры данных особой проблемы не составляет.
Обычно программы представляют собой описание DSL языков, и весь бизнес слой представляет собой такой спецефический язык. При этом всё это проще ложится на REST.
А почему бы и нет?
Вообще-то PHP — это единственный язык, который я могу припомнить, где по одному спец. символу можно понять что и откуда вызывается, весь контекст исполнения и как было задекларировано всё, участвует в искомом контексте: Стрелочка — это контекст инстанса, дабл двоеточие — статика, доллар — это переменная, его отсутствие — это константа (или функция, если заканчивается скобочками) и т.д.
Каждый оператор в PHP тоже однозначен: Это один из немногих языков, где математический оператор сложения коммутативен.
Так что в этом плане дизайн языка практически идеальный, как следствие и читаемость.
Предлагаю оспорить мой тезис и назвать любой другой популярный язык, где бы не требовалось знать состояние и контекст для понимания поведения программы по одной единственной строчке.
доллар — это переменная
Вообще обозначать переменную спецсимволом это страшно далекий пережиток прошлого и в PHP держатся как за нечто основополагающее, по нынешним временам спецсимволом предваряют интерполяцию строк и то это для компилятора, на мой взгляд лишнее, во многих языках понятно где переменная
его отсутствие — это константа (или функция, если заканчивается скобочками)
Опять же константы принято писать прописными символами и это сразу понятно что есть что, насчет функций — в Ruby к примеру даже если у функции будет имя из одной буквы и не будет скобок вы все равно поймете, что это функция, точнее правильнее будет сказать метод, потому что благодаря парадигме «Все есть объект» в Ruby метод будет вызываться на объекте при помощи оператора "." a.b или как там говорят посылается сообщение объекту, и тут же понятно, что первым идет экземпляр класса(переменная), вторым метод.
Это один из немногих языков, где математический оператор сложения коммутативен.
В той же Java или JavaScript математический оператор сложения выглядит куда логичнее и понятнее в конкатенации строк, вместо str1.str2 в PHP, вот где спотыкаешься после Java, JavaScript, Ruby…
Стрелочка — это контекст инстанса
Это тоже вносит путаницу видя стрелку думаешь это стрелочная функция ан нет, это всего лишь вызов метода. В общем много у PHP лишнего в синтаксисе и менять это никто не решается, нету буйных как было с Python 3.0
А можно услышать хоть один аргумент, кроме субъективного "я привык"?
Потому что всё что вы перечислили — это наоборот, совершенно очевидные достоинства языка, например:
- "Доллары" позволяют визуально отличать разные декларации на уровне синатксиса, а не соглашений (мы взяли и решили), плюс реализуют функционал метапрограммирования (ваши отсылки к Ruby, где полей класса вообще не существует в данном случае не состоятельны). Помимо этого они разрешают конфликты имён (new $some и new some — совершенно разные конструкции), которые в некоторых языках вообще не возможны, а где возможны — делаются километром кода через рефлексию (java as example). Т.е. один символ выполняет работы больше, чем 100500 строк кода в любом другом языке.
- Оператор сложения в PHP никогда не меняет сумму (я уже писал выше об этом, это основопологающее свойство этого математического оператора). То что вы ссылаетесь на другие языки, где иначе и где это создаёт кучу проблем (например JS) основано так же на "я привык", а не "будет лучше потому что A, B, C".
- Стрелочки — тоже субъективно. Этот синтаксис взят из плюсов. Опять ни одного аргумента, кроме "я привык так" почему надо сделать как вы предлагаете я не наблюдаю.
ну и так далее.
Т.е. вы предлагаете ухудшить читаемость и удобство языка в пользу "я так привык". Предлагаю постараться забыть про это и написать что-то более объективное, где действительно предлагаемые вами изменения дадут хоть какой-то выигрышь по читаемости или однозначности синтаксических конструкций.
Давайте я вброшу пример такого суждения (не обязательно верного), основанного на аргументах, а не личных привычках:
$this->
можно убрать и расширить синтаксис self\static\parent, оставив конструкции parent->xxx
, parent::xxx
, self->xxx
, self::xxx
, static->xxx
, static::xxx
, т.к. они полностью покрывают весь спектор доступа к методам (родитель, текущий класс, потомок).
- Это позволит избавиться от неоднозначности дизайна
$this
, когда переменная является константой. - Это позволит разрешить проблему дизайна
parent::
, расширив её возможности, где одна и та же конструкция служит как для доступа к методам инстанса, так статическим. - Это позволит снизить семантическую нагрузку на модификатор final методов, который применяется как для запрета переопределения, так и для управления методами от
$this
(т.е. указание контекста на декларативном, а не императивном уровне). Это чуть надуманный аргумент =))
Это тоже вносит путаницу видя стрелку думаешь это стрелочная функция ан нет, это всего лишь вызов метода.
Что это за чушь? Какая стрелочная функция в ПХП?
Ага я знаю что в 7.4 их ввели, но выглядят абсолютно по другому.
В с++ наверное тоже призраки стрелочных функций мерещатся ?
Так и скажите мне нравится %language_name% (или может %languageName%) больше чем php.
Ну и сами себе признайтесь "я не умею писать на пхп и не знаю как оно делается".
Казаться "крутым" у Вас точно не получается )
Так и скажите мне нравится %language_name% (или может %languageName%) больше чем php
Да, нравится:-) Нравится лаконичность, логичность и выразительность. Потом поглядишь после %language_name% на ПХП интереса ради, чего это народ топит за него, дело вкуса конечно. Человек который вошел в ПХП и будет за него держаться, это его мир, непохожий на все остальное:-)
Ну и сами себе признайтесь «я не умею писать на пхп и не знаю как оно делается».
Не, не знаю как это делается, пытался понять, но сам язык отбивал желание.
В с++ наверное тоже призраки стрелочных функций мерещатся ?
С плюсами не сталкивался, когда то давно изучал С
В том же JS использование плюса для конкатенации поражает иногда очень трудно локализцемые баги
Поддерживаю. После PHP чувствую себя неуютно каждый раз, когда приходится на JS или другом языке "суммировать" строки и продумывать, не сложится ли что-нибудь в математическом смысле вместо конкатенации.
Просто надо не использовать то в чем не уверен на 100% если пишешь на таком шатком языке как пхп или жс (ну или перепроверять в консоли)
После прозрения обычно пишут статьи типа "Why I Hate PHP/JS/Whatever" :smile:
С другой стороны, это пережиток прошлого, поскольку разработка ведется в средах разработки, которые дают вам всю эту информацию, используя подсветку синтаксиса и другие подсказки.
В свое время была популярна венгерская нотация именования переменных, когда перед именем указывали тип переменной. Тогда это давало понять тип переменной без необходимости искать ее объявление в коде, но сейчас эту задачу решают среды разработки, не захламляя код.
Зачем писать на PHP в 2020?
Чтобы позлить хэйтеров и получить удовольствие от языка.
Почему вместо PHP не взять тот же Питон?
Ну ладно, Питон иногда тормозной, ну JavaScript?
Ну ладно, JavaScript иногда странный, ну TypeScript?
Ну ладно, TypeScript иногда замороченный, ну Kotlin?
Ну ладно, Kotlin иногда тяжеловесный, ну Go?
Ну ладно, Go иногда невыразительный, ну Rust?
Ну ладно, Rust иногда без нужных либ, ну Java?
Ну ладно, Java иногда навевает тоску, ну Scala?
Ну ладно, Scala иногда слишком сложная, ну Haskell?
Хотя может я ошибаюсь, и кто-нибудь объяснит мне в чём профит от php. А то я, как java разработчик, вижу только минусы(которые связаны с вышеописанными костылями, а не с самим языком). Только не надо про ЗП и кол-во специалистов.
Наиболее близкий к Java :) и с каждым годом всё ближе, хотя, кажется, уже и обгоняет по некоторым вопросам типа union тайп(хинт)ы. Причём не только язык, но фреймворки/библиотеки некоторые явно многое заимствуют из мира Java. Иногда даже интерфейсы совпадают, если игнорировать синтаксис языка типа доллара в именах переменных/параметров.
Иногда ощущение, что средней руки веб-проект без сокетов на Java/Spring/Hibernate я могу написать немногим дольше чем на PHP/Symfony/Doctrine и код будет очень похожим.
Современный PHP — это такой энтерпрайз язык в мире интерпретируемых языков.
Что хорошо в Java — это среда исполнения и возможности отладки, но в этом плане PHP в сторону java не движется, насколько я понимаю.
Вам, видимо, ни к чему. Она нужна тем, кому близка объектная модель Java (без дженириков :( ), но кто не хочет связываться с Java экосистемой. Вот как-то в "продающих" описаниях относительно новых языков, у которых JVM целевой рантайм, таких как groovy, scala, kotlin, подчеркивается, что им доступна вся мощь Java мира прозрачно.
И есть больше подозрение, что если писать на них, игнорируя всё что создано на других JVM-языках, прежде всего на и для самой Java, то "пацаны не поймут", собесдование по groovy или scala не пройдёшь, если не знаешь ничего кроме языка и его "родных" библиотек и фреймворков. Так ведь?
Зависит от языка. На скале, например, вполне нормально игнорировать java подходы. Даже напротив — часть Scala разработчиков считает плохим тоном их использовать. А kotlin напротив, пытается облегчить написание каноничного java-кода.(но и у него есть собственный подход, к dsl, например)
Как минимум, под вопросом зачем тогда эти языки, если можно изучать Java?
Я больше не про подходы в своём коде, а про использование сторонних java-пакетов как библиотек, а то фреймвокров, включая стандартные пакеты Java.
Конкретных примеров не приведу, но, например, допустим, нет в Scala пакета для работы с SOAP, потому что в Java он отличный из коробки. Мало того, вполне может оказаться, что и "родной" scala документаци по использованию условного com.java.soap нет и все примеры только с java, которые надо понять на интуиции, перевести на scala, да ещё временами, переветси так, чтобы уши java подхода не торчали, а то часть разработчиков PR не примут. Ну, не знаю, в чём он там заключается, но, например, вместо циклов по мутабельным объектам цепочки чистых map/filter/reduce и лямбд над коллекциями иммутабельных.
Как минимум, под вопросом зачем тогда эти языки, если можно изучать Java?Что-бы чуть поменьше(или гораздо меньше) страдать при написании и проектировании системы.
Scala, например, заточен под более надёжную типизацию. У этого есть профиты, но, конечно, взаимодействовать с Java кодом приходится. И знать стандартную библиотеку java тоже.
А на kotlin, например, стандартная библиотека java активно используется сама по себе.
Java платформа довольно обширна, тут хватает и скриптовых языков. А что может предложить PHP, например, по сравнению с groovy? Чем он лучше(кроме синтаксиса, или чего-то ещё субъективного)? Он проще? Для хелоуворда да, но PHP это давно не хелоуворды.
Я не считаю что java, как язык, подходит для enterprise. Что объектная модель Java чем-то хороша.
А чем именно по вашему плоха объектная модель java? и почему java не для энтерпрайз?
ООП здесь только мешает, например в момент соприкосновения объектной модели в сущностях hibernate(а это вполне себе объекты, а не просто данные) с анемичной моделью бизнес логики.
Что, например, следует возвращать из сервиса бизнес логики? Entity? Но ведь это прямое нарушение инкапсуляции, утечка абстракций. Какой-нибудь data класс? А как в таком случае стакать ссылки?
Из сервиса бизнес-логики возвращать Entity вполне нормально, по-моему. Сервис приложения берёт на себя конвертацию entity в dto и обратно. Хотя есть и другие мнения.
Это не говоря уже о том, что многие даже Id из БД считают утечкой абстракции.
Для меня Entity — сущность слоя бизнес-логики, где-то под капотом связанная со слоём данных. И в сервисах приложения вполне нормально изменять состояние сущности через её мутаторы и это изменение должно быть сохранено в общем случаем. Вот есть у нас сущность Contract c полем status и методом activate, который изменяет статус на 'active'. Как по мне, то вполне нормальный алгоритм для сервиса активации договора по id такой:
- из репозитория вытаскиваем сущность по id (скаляр или ValueObject)
- вызываем её метод activate()
- (опционально) возвращаем DTO, собранное из сущности
Где тут нарушение инкапсуляции? Сервис получает параметрами не доменные объекты, отдаёт не доменные объекты.
Для меня Entity — сущность слоя бизнес-логики, где-то под капотом связанная со слоём данных.Но по факту это не так. Entity это слой persistence, и я уже объяснил почему.
Где тут нарушение инкапсуляции?Не очень понял Ваш кейс, т.к. описано что происходит, но не описано где. Если имелось в виду что из слоя бизнес логики возвращаются DTO, то инкапсуляция тут не нарушается(правда получение контракта по id это тоже утечка абстракции, здесь следовало бы использовать номер контракта, либо UUID; но часто это бывает неудобно).
Зато сразу возникают вопросы со ссылками на другие сущности. Как их делать в DTO? Через id? Что-то совсем не ООП получается — у нас просто методы с контекстом(сервисы бизнес логики) и структуры данных(Value Object). Но в целом с таким подходом я согласен.
Но по факту это не так. Entity это слой persistence, и я уже объяснил почему.
По факту, Entity слой бизнес-логики, прозрачно декорированный слоём persistence. Стараются, чтобы совсем прозрачно, но не всегда получается в обе стороны: и для клиентов бизнес-логики (слоя приложений) есть нюансы, и слой persistence протекает бывает (все эти pre/post методы, ну и аннотации)
Не очень понял Ваш кейс, т.к. описано что происходит, но не описано где.
Сущности и интерфейсы репозиториев в слое бизнес-логики. На практике с ORM аннотациями, но в принципе маппинг можем закинуть в отдельные файлы, xml например, в слое persistence. Реализации репозиториев там же, в persistence. В слое приложения/юзкейсов вот именно эти три строки:
entity = this.contractRepository.getById(id);
entity.activate()
return ContractDTO.createFromEntity(entity);
Получается, что слои presenter и прочие UI доступа к сущностям не имеют, только их непосредственные клиенты из слоя apllication/usecases.
Если имелось в виду что из слоя бизнес логики возвращаются DTO, то инкапсуляция тут не нарушается
Нет, тут имелось в виду, что из слоя бизнес-логики (интерфейса репозитория) возвращается сущность (прозрачно обёрнутая persistence слоём), а уже слой сервисов приложения для слоя UI (http контроллеров, например) возвращает DTO. Считать ли это нарушением инкапсуляции — зависит от определения. Я не считаю, если у сущности публичные только бизнес-методы типа activate, pay и т. п., если сущность гарантирует соблюдение своих инвариантов, а не просто класс с данными, обвешанный геттерами и сеттерами. Публичных сеттеров на все свойства по умолчанию такой подход не предполагает. Только там, где это не нарушает инкапсуляцию, где setЧтотам относится к бизнес-логике, где это set* вплоть до UI "протекает". А так, к сеттерам, если они есть, доступ только со слоя бизнес-логики.
Некоторые предпочитают изолировать слой бизнес-логики сильнее, по сути создавая фасады для неё, которые принимают и отдают только скаляры/DTO. С таким вариантом код выше будет в этом (под)слое изоляции, ACL ещё зовут иногда. Но я считаю. его обычно избыточным.
Зато сразу возникают вопросы со ссылками на другие сущности. Как их делать в DTO? Через id?
В зависимости от потребностей клиента. Может ContractDTO.customerId, может ContractDTO.customer типа CustomerDTO. Ну и DTO по определению, насколько я помню, никакой особой логики не должны содержать. Вся бизнес-логика логика в сущностях и бизнес-сервисах. Их публичные методы гарантируют соблюдение бизнес-правил модели бизнеса. Во втором подходе, с фасадами, у сущностей публичных методов нет, только сервисы бизнес-логики/фасады могут их дёргать. А DTO лишь для передачи данных между слоями приложения и UI в первом варианте, и бизнес-логики и приложения во втором.
По факту, Entity слой бизнес-логики, прозрачно декорированный слоём persistence.Я так не считаю, здесь мы расходимся во мнениях.
Вся бизнес-логика логика в сущностях и бизнес-сервисах. Их публичные методы гарантируют соблюдение бизнес-правил модели бизнеса. А DTO лишь для передачи данных между слоями приложения и UI в первом варианте, и бизнес-логики и приложения во втором.DTO в любом случае существуют, а значит нам всё равно придётся уходить от ООП, явно разделяя функции(методы, функции с контекстом) и данные. Это то, о чём я говорил в начале — концепция объединения данных и поведения в итоге не работает. Мы получаем данные, смешиваем их с поведением, что-бы потом вновь разделить. В итоге мы просто делаем лишнюю работу.
DTO — лишь средство передачи данных, можно заменить вообще на json или другую сериализации-десериализации. ООП работает в, прежде всего, сущностях, в основных элементах бизнес-логики, если не делать из них опять же дата классы, относящиеся к персистенс слою.
Json или dto — не принципиально, все равно в итоге идет строгое разделение данные/функции
Нет, оно не строгое. DTO лишь для изоляции слоёв, для инкапсуляции бизнес-логики в выделенном для неё слоё. В терминах программирования — для передачи объектов по значению, а не по ссылке.
В терминах программирования, то что Вы описали это именно строгое разделение на функции и данные.
Нет. DTO — лишь технический момент для страховки от нежелательных вызовов мутаторов и задел для будущего межпроцессного (читай сетевого) взаимодействия. Обработка данных DTO заключается лишь в создании полноценных объектов на базе данных из DTO. Всё. Собственно обработка бизнес-данных идёт в, прежде всего, методах сущностей.
Да не требует оно строгого разделения на данные и функции, их обрабатывающие. Берём данные из базы, гидрируем ими объекты с методами их обработки, запускаем эти обработки, выделяем обработанные данные из объектов, записываем их в базу и опционально отдаём в клиентский процесс.
Основная обработка, суть исполняемого этапа бизнес-процесса, как раз в бизнес-методах, тесно связанных, инкапсулированных с бизнес-данными.
Но по факту все проблемы, озвученные мной в силе.
Разделение на данные и функции не требуется — но REST по другому не работает.
Сущности hibernate всё ещё относятся к слою данных, и никто в них поведения(связанного с логикой) не закладывает. Это считается плохим тоном, да и бинами spring эти сущности не являются.
Единственный нормальный вариант — это возвращать ValueObject'ы из сервисов бизнес логики. И у нас просто функции + данные. Но даже если этого не делать — всё равно разделение всплывает на уровне REST.
В итоге проблемы нет — правда приходится делать 10 слоёв с преобразованиями, получая не ОО код/api, но это так — мелочи. ООП, что бы это ни было, работает!
Никто — слишком категорично чтобы быть правдой. Вот навскиду https://github.com/ChristophKnabe/spring-ddd-bank/blob/master/src/main/java/de/beuth/knabe/spring_ddd_bank/domain/Client.java#L107 — чисто бизнесовый метод deposit в сущности, hibernate насколько я могу судить.
REST обычно ограничивается даже не слоём приложения/юзкейсов, а слоем презентации/UI/портов и адаптеров. Причём к одному и тому же сервису приложения/юзкейса могут быть привязаны как REST http контроллеры, так и grpc, graphql, *MQ/Kafka, Cli и чёрт знает что ещё.
Я уже сказал, это считается плохой практикой. Как минимум логика размазывается между сервисами и объектами слоя данных.
Ирония в том что почти все перечисленные технологии оперируют не с объектами.
А можно подтверждение, желательно с обоснованием, почему это плохая практика, когда логика имеет слабую связанность (coupling) и высокую связность(сohesion)?
Чему нужно подтверждение? Что бизнес логика не должна находиться в слое persistence?
Что бизнес-логика не должна находиться в Entity, а должна находиться в сервисах, даже если всё, что делает сервис — вызывает один метод Entity.
Нет, начинал я не с hibernate, тут другие соображения.
Я отошел от представления о том, что ООП лучше чем не ООП, и(в следствии) об анемичной модели как об антипаттерне.
ООП (данные вместе с их методами обработки) тут удобно тем, что можно вынести из вышележащего кода ответственность за работу с конкретным набором данных. Это можно сделать в любом ЯП функциями, но в некоторых приятнее. Если не делать этого, то между сервисами легко получить data coupling.
Есть и критика подхода, исповедуемого как ORM, так и общим дизайном систем, но она не про rich модели. Дело в том, что ORM тут маппит таблицы на объекты, таблицы делаются по прикидке разработчика, и легко получается, что один кусок данных нужен в другом месте, для чего нужно затащить всю модель, много логики приходится оставлять в сервисах, и из приложения легко получается запутанный комок грязи с анемичными моделями.
Почему был? Его объявили лучшей практикой, а rich model антипаттерном?
Просто пока я считаю что пользователи слоя бизнес логики не должны знать про hibernate, жизненный цикл сущностей и прочие вещи, связанные с БД(А это _обязательно_ нужно будет знать).
И сохранить сущность как простой интерфейс взаимодействия с БД, без собственного поведения, видится мне правильным.
Ну вот собственно задача hibernate и подобных ORM — сделать прозрачной работу с базой данных для, прежде всего, самого слоя бизнес-логики. Чтоб он о ней не знал ничего, максимум аннотации в исходниках сущностей, которые заменяются если хочется на другие форматы описания маппинга, на XML, например. Чтобы весь набор сущностей, которыми манипулирует бизнес-логика были полноценными объектами, отражающими бизнес-логику в терминах бизнеса. Анемичная модель заставляет вводить в программный код абстракции, которых в реальном мире (в том числе в UI) не существует и которые даже технически ничем не обусловлены кроме желания сделать сущности ORM простыми DTO передающими данные в БД из слоя бизнес-логики, атрофировавшегося в слой бизнес-сервисов
Анемичная модель заставляет вводить в программный код абстракции, которых в реальном мире (в том числе в UI) не существуето каких абстракциях речь? Думаю, анемичность модели никак не коррелирует с реальностью описываемых ей объектов.
которые даже технически ничем не обусловлены кроме желания сделать сущности ORM простыми DTO передающими данные в БД из слоя бизнес-логики, атрофировавшегося в слой бизнес-сервисова кто сказал, что здесь должно быть именно техническое обоснование?
Могу предположить что анемичная модель обычно стремится к stateless. В то время как rich больше про модификацию состояния сущности, что в свою очередь накладывает отпечаток на тестируемость и работу в многопоточной среде.
Как, например, передать сущность, по которой были проведены какие-то операции, в другой поток, для продолжения дальнейших действий?
Плюс нужно всегда помнить, что сущность управляется entityManager, и это обусловлено именно persistence.
о каких абстракциях речь? Думаю, анемичность модели никак не коррелирует с реальностью описываемых ей объектов.
Сервис в бизнес-слое для элементарных операций над одной сущностью, не затрагивающих ничего больше.
а кто сказал, что здесь должно быть именно техническое обоснование?
А какое у вас? Идеологическое? :)
Могу предположить что анемичная модель обычно стремится к stateless. В то время как rich больше про модификацию состояния сущности
Стейт один и тот же. Разница лишь в том торчит он кишками наружу благодаря куче сеттеров или наружу только бизнес-методы, которые гарантируют непротиворечивое состояния сущности или кишками сущности управляет специально обученный сервис.
Как, например, передать сущность, по которой были проведены какие-то операции, в другой поток, для продолжения дальнейших действий?
Тут не подскажу, с реализацией многопоточности в Java не знаком.
Плюс нужно всегда помнить, что сущность управляется entityManager, и это обусловлено именно persistence.
Вот на уровне бизнес-логики как об этом помнить не обязательно в идеале. Да и в целом правильней выражение "персистентностью сущности управляет entityManager" — он может удалить её из базы, но не может удалить из памяти — максимум из своих внутренних записей типа IdentityMap удалить ссылки на неё. А создать вообще не может, только добавить ссылку переданную снаружи.
Сервис в бизнес-слое для элементарных операций над одной сущностью, не затрагивающих ничего больше.Сервис это деталь реализации, такая же, как какая-нибудь фабрика или тот же entityManager. По сути сервис представляет собой просто набор функций, которые, безусловно, основаны уже на реальных операциях над данными.
А какое у вас? Идеологическое? :)Видимо да, такое же, как и у вас.
Стейт один и тот же«кишками наружу» означает, что стейт всё-таки по большей части отделён от функций. Его проще, например, подменить для тестов. Или использовать для расчётов, не изменяя настоящую сущность. Например, я хочу посмотреть что поменяется, если я изменю в контракте один метод начисления процентов на другой. Как отработает функция на этом контракте. В rich модели мне придётся отдельно позаботиться, что-бы изменения не попали в БД.
Вот на уровне бизнес-логики как об этом помнить не обязательно в идеале.приходится вспоминать, когда всплывают проблемы с ленивостью, например.
Видимо да, такое же, как и у вас.
Ну, я считаю, что всё-таки rich модели обусловлены такими объективными принципами как уменьшение связанности и увеличение связности объектов. Обычно как минимум часть методов можно перенести из бизнес-сервисов в сущности, уменьшая количество связей между сервисами и сущностями, вплоть до избавления от сервиса. Ну и соблюдение инкапсуляции:
вот есть сущность договора, при подписании его с нашей стороны нужно фиксировать его статус "подписан" и, собственно, кем с нашей стороны он подписан. С анемичной моделью будет в сервисе что-то вроде
public void signContract(Contract contract, User user) {
if (contract.status != ContractStatus.Draft) {
throw new DomainException("Only draft contract can be signed");
}
contract.setStatus(ContractStatus.Signed);
contract.setSigner(user);
}
а в application сервисе
public void signContract(ContractId contractId) {
Contract contract = this.contractRepository.get(contractId);
User user = this.context.getCurrentUser;
this.contractService.signContract(contract, user);
}
В случае же rich сущности, мы прямо в сущности имеем
public void sign(User user) {
if (this.isDraft()) {
throw new DomainException("Only draft contract can be signed");
}
this.status = ContractStatus.Signed;
this.user = user;
}
и в сервисе приложения
public void signContract(ContractId contractId) {
Contract contract = this.contractRepository.get(contractId);
User user = this.context.getCurrentUser;
contract.sign(user);
}
Мы уменьшаем количество связей. Сервис приложения ничего не должен знать о contractService (может даже потому что его вообще нет), enum ContractStatus внутренний для Contract, геттер `ContractStatus getStatus()` и множество проверок в коде вида `contract.getStatus() != ContractStatus.*` ,заменены на иззеры `boolean isDraft()`, `boolean isSigned`, которые в слое приложения часто можно не проверять, потому что сущность сама выбросит исключение при попытке нарушить бизнес процесс.
Как по мне, то вполне технические преимущества :)
Таким образом я могу получить представление SignedContract, например, у которого будут заполнены поля signatory и signDate, не имеющие смысл при других состояниях контракта. И сделать функции с другими контекстами(сервисы), которые оперируют только с подписанным котрактом, например.
Плюс я могу сделать отдельный сервис для расчётов, который будет рассчитывать, например, графики платежей для разных состояний (кредитного)контракта, не изменяя при этом его реальное состояние.
В rich же модели сложнее сказать что и когда меняется в реальном состоянии, тем более если есть ссылки на другие сущности.
Это вполне конкретная программная сущность. Не сущность предметной области, а программная как переменная или функция.
А класс Contract — это программная модель сущности Договор предметной области. А представления этой модели действительно могут быть разные для разных юзкейсов.
И rich модель не ставит запретов на создание сервисов, он предполагает лишь, что в большинстве случае в рамках одного метода вызывается только один метод сущности, если утрировать. Ну и нужность сервисов, в методах которых только методы сущности и вызывается и ничего больше, под большим вопросом.
Это вполне конкретная программная сущность. Не сущность предметной области, а программная как переменная или функция.И? Методы rich модели тоже программная сущность. Можно вообще не создавать программные сущности, просто делать прямые запросы sql. Так что ни разу не аргумент.
И rich модель не ставит запретов на создание сервисов, он предполагает лишь, что в большинстве случае в рамках одного метода вызывается только один метод сущности, если утрировать. Ну и нужность сервисов, в методах которых только методы сущности и вызывается и ничего больше, под большим вопросом.Так речь не о запретах создания сервисов. Речь о том, что rich модель усложняет работу с состояниями для пользователей бизнес слоя.
И? Методы rich модели тоже программная сущность.
Именно, сущности сильно связные с данными модели, в идеале исключающие необходимость выдавать эти данные наружу объекта
Так речь не о запретах создания сервисов. Речь о том, что rich модель усложняет работу с состояниями для пользователей бизнес слоя.
С одной стороны, если сильно изолируем бизнес-слой за фасадом, принимающим и отдающим скаляры и DTO, то вообще без разницы — интерфейс бизнес слоя не меняется. С другой, хоть с изоляцией, хоть без, если нам нужно работать с состоянием, то чем усложнит разбитие работы с состоянием на два класса — анемичную модель сущности и бизнес-сервис, обеспечивающий инварианты сущности, когда можно обойтись одним — полноценной работой сущности.
когда можно обойтись одним — полноценной работой сущности.
Не получится одним обойтись. Либо зависимости понадобятся для бизнес-логики, которые сущность сделают сильно связанной, либо найдется поведение, которое в рамки одного агрегата не влезет. Не панацея.
Когда-то получится избавиться совсем, когда-то значительно сократить количество и размер методов в бизнес-сервисах, когда-то ничего не получится. Но в целом тренд в сторонц сокращения и упрощения с переходом на рич модели.
Все таки это отличается от процедурного кода, так как сервисы не используют глобальное состояние, их можно тестировать изолированно. А разделение на сущности и сервисы никуда не денешь, так как это природа вещей, сама предметная область подразуевает наличие данных.
Когда вижу подобный код — данные и stateless сервисы — у меня один вопрос: зачем тут ООП язык взяли?
Данные-то понятно, что должны быть, но зачем их отделять от методов их обработки, когда можно не отделять и получать гарантии, которые при разделении недостижимы малой кровью.
Отделять их затем, что если бизнес-логику в сущности помещать, то она станет перегруженной рано или поздно, с ростом количества кейсов использования сущности и, соответственно, ростом количества бизнес-методов сущности. С сервисной декомпозицией такого не произойдет. У сущности одно дело в приложении — представлять бизнес данные, и хватит с нее.
Возможно, как вы говорите ООП мешает, но какие у нас альтернативы?
Анемичные модели, dto vs entity это проблемы не java, а в целом ООП подходаневозможно говорить за весь ООП, ведь нет чёткого определения что это вообще такое. По этому я говорю о самом популяром варианте — java-style ООП(которое изначально пошло из С++, и корнями уходит бог знает куда).
Возможно, как вы говорите ООП мешает, но какие у нас альтернативы?Как минимум есть ещё функциональный подход.
В английском проще, у них есть OOD и есть OOP, и то, и другое переводится на русский как ООП, просто в одном случае это проектирование, а в другом программирование. Анемичность или нет — это к проектированию, как разделять ответственность между объектами, а java-style — к программированию, как реализовать эти ответственности.
Совмещение в одной сущности данных и логики работы с ними прежде всего, буквальная инкапсуляция, а не часто подразумеваемое под ней сокрытие. Наследование и полиморфизм важны, но не критически, особенно если не считать наследованием и полиморфизмом несколько реализаций интерфейсов.
Могут быть отклонения типа DTO/VO или stateless сервисов, но это именно отклонения. Если таких отклонений большинство в программе, то
Наследование даже не упоминаю т.к. это всего-лишь механизм, а не концепция.
Полиморфизм _подтипов_ — это то, что выделяет ООП? Но ведь есть ещё миллион разных полиморфизмов, почему бы под каждый не сделать N-ориентированное программирование?
Могут быть отклонения типа DTO/VO или stateless сервисов, но это именно отклонения. Если таких отклонений большинство в программе, тоВ последнее время такой дизайн я встречаю чаще, чем «объектно ориентированный».
С начала времён существовали различные вещи вроде дескрипторов, они тоже Объектно Ориентированны, получается?
Вот как раз переход от передачи дескрипторов в функции к инкапсуляции дескриптора и функций в одной программной сущности и есть переход от ПП к ООП. Один из аспектов этого перехода, точнее.
Полиморфизм подтипов — это то, что выделяет ООП?
Не выделяет, а образует. ООП — достаточно конкретная комбинация принципов инкапсуляции, наследования (как механизма образования подтипов объектов) и полиморфизма этих подтипов.
В последнее время такой дизайн я встречаю чаще, чем «объектно ориентированный».
Я в последнее время вообще редко встречаю следы продуманного дизайна, кроме навязанного выбранного платформой. :( Иногда навязывание даже не техническое, а просто примеры в документации для демонстрации одного аспекта, трактуется как универсальные, что так надо писать всегда.
Вот как раз переход от передачи дескрипторов в функции к инкапсуляции дескриптора и функций в одной программной сущности и есть переход от ПП к ООП. Один из аспектов этого перехода, точнее.По мне так никакого перехода не было. И ООП суть ПП, за исключением некоторых не принципиальных нюансов.
ООП — достаточно конкретная комбинация принципов инкапсуляции, наследования (как механизма образования подтипов объектов) и полиморфизма этих подтипов.Наследование совсем не обязательно, иерархию подтипов можно организовать с помощью утиной типизации, например.
В итоге у нас просто полиморфизм подтипов + инкапсуляция. Причём инкапсуляция совсем не уникальна для ООП, так что с конкретикой я бы поспорил. ООП это не что-то конкретное и чётко определённое.
Я в последнее время вообще редко встречаю следы продуманного дизайна, кроме навязанного выбранного платформой. :( Иногда навязывание даже не техническое, а просто примеры в документации для демонстрации одного аспекта, трактуется как универсальные, что так надо писать всегда.Тут полностью соглашусь, это как раз и стало отправной точкой в моих сомнениях насчёт ООП как парадигмы.
По мне так никакого перехода не было.
А я как сейчас помню свой восторг от перехода с C на C++, хотя уже почти 25 лет прошло. Я уже экспериментировал с инкапсуляцией, с каким-то аналогом объектов на структурах с указателями на функции, кажется в gtk что-то похожее сделали. Но С++...
Причём инкапсуляция совсем не уникальна для ООП, так что с конкретикой я бы поспорил. ООП это не что-то конкретное и чётко определённое.
Не чётко, да, но достаточно обычно, чтобы отличать ООП от чего-то другого. Вот в Go почти ООП
Почти ООП, недоооп. Вы наверное знаете что и Java это не ООП, ведь в смолтолке все не так. Не существует единого определения ООП, как и самого понятия. А про переход — его небыло не потому, что не появлялись языки с новым на то время маркетинговым термином. Аналогичные подходы были всегда там, где они уместны. Современное же ООП — это пихание одинакового подхода со смешиванием функций и данных туда, где это не работает и не нужно. И смещение фокуса с реальных проблем на разделение ответственности между объектами.
Анемичные модели, dto vs entity это проблемы не java, а в целом ООП подхода
Это не проблема ООП подхода. Это проблема программистов, его использующих, недостатка их олыта.
Логика в моделях в первую очередь должна быть связана непосредственно с данными модели, как-то – валидация данных, констрейнты. Там где логика завязана на несколько моделей, в дело вступают сервисы.
В целом согласен, но еще бы учтонил, что бизнес-методы не должны быть в сущностях и агрегатах, они должны быть в сервисах. Сущности это слой данных по сути своей и их задача представить данные в приложении (оффтоп: поэтому DataMapper необязателен, с таким подходом можно и dao и row gateway использовать, все хорошо будет).
Почему не должны-то? Суть агрегата в том, что он гарантирует целостность, валидность всех сущностей, входящих в него в любой момент времени, в том числе путём неотдачи этих сущностей наружу. А сущности гарантируют свою целостность путём выставления наружу не сеттеров для всех полей, а методов, гарантирующих соблюдение инвариантов.
Собственно DataMapper и нужен для того, чтобы с сущностями такими работать, а с анемичной моделью он избыточен.
Почему не должны-то?
Я уже объяснил свою позицию в другой ветке. Повторюсь. Если бизнес-логику в сущности помещать, то она станет перегруженной рано или поздно, с ростом количества кейсов использования сущности и, соответственно, ростом количества бизнес-методов сущности. С сервисной декомпозицией такого не произойдет. У сущности одно дело в приложении — представлять бизнес данные, и хватит с нее. Инварианты держать — это задача всего домена, не только сущностей и агрегатов.
Инварианты держать — это задача всего домена, не только сущностей и агрегатов.Всё верно. Инварианты определённого набора данных — в сущности. Инварианты, связывающие разные сущности — в сервисе. Об этом и в DDD идёт речь.
При этом в идеале можно спроектировать boundaries так, что все инварианты и процессы вообще лежат в одной сущности-агрегате, потому что все данные для бизнес-процесса уже есть в ней, а данных для других бизнес-процессов нет. Эдакая идеальная SOA.
Ну тогда Java — тоже интерпретируемый язык. Тоже компилируется в байт-код и тоже исполняется на виртуальной машине.
В Java компиляция основа процесса. В PHP — подкапотная оптимизация, сегодня есть, а завтра нет
2. Когда производишь компиляцию Java кода, то ты преобразуешь код программы в байт-код, при этом проверяются типы дынных и корректность кода. На JVM поступает байт-код но не машинный (бинарный) код!
3. PHP так же при старте скрипта вначале компилируется программный код в байт-код и в ZVM так же поступает байт-код, точно так же как и на Java.
4. До версии 7.1 (могу ошибаться, поправьте если не прав с версией) при компиляции в байт-код оптимизации не происходило, сейчас же оптимизация проводится компилятором байт-кода, который никуда не денется по определению!
5. OPcache — простыми словами, это хранилище раннее откомпилированного байт-кода на PHP. Вот как вы думаете, почему при включенном OPcache если меняешь код, то изменения не применяются сразу и надо перезапускать php-fpm (если работаете с его использованием)
По компиляции вывод — принцип работы Java и PHP одинаковый, выигрывает тот, кто лучше производит оптимизаци.
Преимущества Java на данный момент:
1. JIT компилятор
2. Процесс сидит в памяти и не требуется повторной инициализации ресурсов (подключений к БД и т.п.)
Что ждет PHP:
1. JIT компилятор при использовании которого, по предварительным тестам, производительность PHP сравнима с производительностью С.
2. Уже есть сервера на PHP, самый производительный это Swoole (только не забывайте, писать код уже надо не как вы обычно это делаете на PHP)
3. Есть статические анализаторы которые проверяют все ваши типы данных и корректность кода, точно так же как и у компилируемых языков. Но это уже отдельная тема.
В коды реальной машины или виртуальной компилируется код значения не имеет для деления на интерпретируемые или компилируемые.
Байт-код он по определению бинарный. Просто у Java нет (точнее есть, но не популярно) железа, которое может его понимать напрямую.
Вот именно, что при старте скрипта он под капотом компилируется. Пока к скрипту не обратился — это чисто исходный текст. Деплой PHP приложения — это загрузка исходников, никаких бинарников мы не загружаем. Чтобы не тратить время сервера на компиляцию в самый неподходящий момент, нам нужно пройтись по всем исходникам и дернуть их. И молиться чтобы кэша опкодов хватило ) Подготовить байт код для приложения на билд-сервере и загрузить только его, без исходников, на продакшен, штатной возможности нет.
С настройками по дефолту у работает без перазагрузки. Перезапускать надо если специально их отключил и/или включил прелоад. Ну и слово cache намекает, что это внутренняя оптимизация
По компиляции вывод: пока у нас нет штатной возможности подготовить байт- код ZendVM на одной машине и только его, без исходников, загрузить на целевую машину, говорить о PHP как о компилируемом языке преждевременно, даже не смотря на то, что для оптимизации под капотом он использует техники, характерные для компилируемых языков, включая JIT.
Возможно сейчас это даже относительно легко реализовать, но что это будет штатно доступно когда-либо никто, вроде, не анонсирует.
Какие баги и глюки? За 20 лет на PHP с багами и глюками самого языка встречался максимум раз 5 и только один раз пришлось делать воркараунд, а не просто обновить патч.
И на Java сложнее разрабатывать веб-приложения, как по мне, особенно без фреймворков. И я даже не про многословность синтаксическую — всё в классах, типы везде (автовывод вроде недавно добавили и только для переменных, да?)
Экосистема сложнее. В PHP только composer по идее нужен, чтобы современное приложение поднять. В Java так не получится, насколько я знаю. Куча конкурирующих продуктов просто чтобы на локалхосте поднять helloworld
Вот как раз из общения… Maven, Gradle, Ant, TomCat, Spring, Spring Boot, разные Java RTE/SDK, непонятно чем отличающиеся, разные GC, тюнинг параметров ВМ — то, что запомнил, потому что уже краем уха слышал или даже сталкивался. Ну и минимум часть из них определяется языком: язык, компилируемый в байт-код виртуальной машины.
Для одноклассников может Java и хороша по совокупности метрик. Но проектов такого масштаба на постсоветском пространстве хорошо если пальцев хватит пересчитать, а у остальных метрики. И я сильно подозреваю, что у одноклассников Java даже если и основной язык на бэке, то не единственный.
Так в Java достаточно gradle/maven и springboot. Идея сама сгенерирует проект, так что непонятно чем php тут проще. Плюс практически из коробки все готово для микросервисов. И в итоге получится один jar, для запуска которого нужна только java.
А многословность это действительно проблема java, особенно в сочетании с такой простой системой типов.
Ну вот открыл Идею, выбрал новый проект и она предлагает на выбор только одной Java три штуки ("голая", Enterprise, FX). Плюс Spring отдельно, плюс Maven отдельно, плюс Gradle отдельно. Что мне нужно выбрать, что бы создать простой CRUD http json API, слушающий запросы на 80-м порту? Пускай без ORM для упрощения (хотя галочку Hibernate я видел уже и знаю, что Hibenate мне должен показаться простым и понятным), голый SQL, который я ручками напишу. Допустим создал нужный, написал, нажал Shift-F10, локально заработало. Как мне теперь это на продакшен деплоить? Искать где-то на ФС этот jar файл, на голый линукс установить java (или контейнер с JRE), скопировать этот jar и запускать? А если хочу CI/CD, то так же контейнер с JDK и javac .
, а потом откуда-то jar запускать? Кстати, а если hibernate всё-таки выберу, то мне его нужно на сервер отдельно загружать в определнное место или javac его вбилдит в jar?
А если хочу обычный html сайт, то искать библиотеку шаблонизации и обязательно учить её синтаксис? Или вон по старой памяти были JSP, которые требовали установки какого-то софта на сервер, кроме самой java, причём, насколько я помню, не как зависимость проекта, а как его рантайм, по типу php-fpm
Я правильно описал процесс построения приложения и процессов деплоя и знания пары команд java/javac достаточно или всё несколько сложнее?
ЗЫ Если что это не просьба о помощи, чтобы описать мне правильный современный подход в деталях. Скорее, я хочу убедиться, что я правильно понимаю и современные процессы разработки на java куда сложнее туториалов типа "напишите в блокноте код, запустите javac, чтоб скомпилировать, а потом java web-server.jar, чтобы запустить веб-сервер". Но если вдруг детально ответите, а детали уложатся в несколько строк, то буду удивлён и пересмотрю своё отношение.
Про деплой как-то так, но никаких библиотек тащить не нужно, плагин springboot сам всё запечатает в jar. То же самое и с шаблонизатором, это просто библиотека и она будет в jar. CI/CD обычно делается сборкой докер image, в котором уже будет приложение. Есть плагины для систем сборок, которые добавляют task, собирающий всё приложение в контейнер в один клик. Для запуска достаточно развернуть контейнер. Внутри будет и java и jar, плюс параметры легко передаются через env.
Но вот как раз Springboot нет, есть Spring и Spring Initializer.
Вот такого докерфайла будет достаточно для 90% процентов приложений:
FROM openjdk:15
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
RUN javac Main.java
CMD ["java", "Main"]
Или он только для "хелловорлдов" из одного файла без зависимостей от чего либо кроме стандартной библиотеки?
Spring InitializerЭто оно и есть
Или он только для «хелловорлдов» из одного файла без зависимостей от чего либо кроме стандартной библиотеки?Только для хелловорлдов.
Система сборки должна собрать jar, который затем нужно поместить в контейнер. Для gradle, например, можно использовать плагин com.bmuschko.docker-spring-boot-application, который сам всё соберёт и запушит, если надо.
Запустил, на следующем шаге выбор "maven project, maven pom, gradle project, gradle config" — кто все эти люди? Шутка. Выбрал первый — следующий шаг выбор из сотни галочек, все пропустил, проект создан, в ридми — ссылки на документацию мавен, а не спринг, почему-то. Нажал билд — минут на 10 задумался, нажал ран, вывели какую-то диагностическую инфу и exit code 0.
То есть нужен контейнер с gradle, в который нужно установить плагин, запустить его как-то и он соберёт другой контейнер с JRE и jar файлом?
Наверное, хватит?
То есть нужен контейнер с gradle, в который нужно установить плагин, запустить его как-то и он соберёт другой контейнер с JRE и jar файлом?Зачем Вам контейнер для Gradle? Gradle это система сборки, с помощью неё вы создаёте jar'ники, запускаете тесты и т.д. Выбор «gradle project» создаст Вам проект с этой системой сборки, выбор maven создаст с мавеном. Там, наверняка, тоже подобный плагин есть, просто я им не пользуюсь. Всё что потребуется — прописать плагин в файле конфигурации gradle.
Наверное, хватит?Может быть.
Кстати, раз уж тема про контейнеризацию — какой нормальный способ засунуть PHP приложение в контейнер? Что-бы канонично — один контейнер, без всяких отдельностоящих php-fm'ов и nginx'ов?
Ну, есть CI/CD система, которая в своих шагах запускает контейнеры, которые что-там делают для пайплайна. Как я понимаю, нужен либо контейнер для gradle, который генерирует jar, который потом ляжет в контейнер приложения, либо gradle, котоырй прямо сгенерирует нужный контейнер.
В последней время канонично я пихаю php-fpm в контейнер, к которому обращается ingress прямо по fastcgi :) А так, для CMS каких-нибудь предпочитаю отдельные контейнеры для php-fpm и nginx и как раздаватель статики, и как адаптер http<->fastcgi. Некоторые ставят apache+mod_php. А некоторые веб-сервер поднимают на ReactPHP и ко.
Вариант с двумя процессами в контейнере мне не нравится, как и вариант php -S
(встроенный веб-сервер php-cli)
Ну, есть CI/CD система, которая в своих шагах запускает контейнеры, которые что-там делают для пайплайна. Как я понимаю, нужен либо контейнер для gradle, который генерирует jar, который потом ляжет в контейнер приложения, либо gradle, котоырй прямо сгенерирует нужный контейнер.Ну вообще там достаточно запустить ./gradlew dockerPushImage, например. Он в самом проекте есть. Из контейнера или нет — не особо важно, главное что-бы java была установлена.
В последней время канонично я пихаю php-fpm в контейнер, к которому обращается ingress прямо по fastcgi :) А так, для CMS каких-нибудь предпочитаю отдельные контейнеры для php-fpm и nginx и как раздаватель статики, и как адаптер http<->fastcgi. Некоторые ставят apache+mod_php. А некоторые веб-сервер поднимают на ReactPHP и ко.ну да, я сейчас так и сделал — 2 контейнера, nginx + php-fm. Но не очень-то удобно, что статику нужно отдельно пихать в nginx. Думал может есть каноничный путь какой-нибудь. В принципе и так норм.
Если классическое PHP-приложение, отдающее html и статика рядом, то использую докерфайл типа:
FROM php AS build
COPY . ./
RUN bin/build
FROM php:fpm AS app
COPY --from=build src vendor config ./
FROM nginx AS web
COPY --from=build config/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build public ./
Для тех задач которые решает PHP она просто хуже подходит. Если и выбирать что то вместо PHP то это Python, Nodejs либо Ruby. И в каждом из этих выборов есть свои плюсы и минусы.
Мое сугубо личное мнение по поводу Java. Она многословна и прожорлива. В эпоху докера ее модель виртуальной машины — бессмысленный рудимент (по крайней мере для backand).
Если говорить про PHP у него хватает проблем. Общая область видимости встроенных функций которая затрудняет deprecate встроенных пакетов. И доставшееся с первых версий неявное подключение файлов через __autoload(). Которое делает зависимости между компонентами системы неявным. Но ни один нормальный программист не будет делать этого. И будет использовать жестко описанный в PSR стандарт подключения файлов который решает эту проблему. Плохо можно писать на любом языке. Да в PHP чуть проще написать плохой код. Но тут куда важнее слово «чуть»
Если ещё не начали изучать никакой язык и сомневаетесь в выборе, то подумайте с точки зрения владельца среднестатистического бизнеса, который будет нанимать вас на работу. Для них идеальный работник — 15 летний школьник, который работает «за еду». А худший выбор для них — 50 летний бородач, который кодит на «ассемблере» как боженька. Школьник получит работу, а бородач будет безработным. Так потоки денег сами решат, какой язык будет популярным.
Не надо «холиварить» за язык программирования. Думайте о деньгах. Всё остальное — пыль.
Но если разрабатываешь ПО в конкурентной среде, то индусы должны быть более образованными, что бы не потонуть в говнокоде, догоняя конкурентов и разрабатывая новые фичи. Не все так однозначно, имхо.
У «бородача» будет 1-2 работодателя непосредственно на выбор. И еще пяток работодателей по всему миру будут целыми HR-отделами «охотится» за таким «бородачом».
А вообще, если это IT-бизнес — будут сразу стараться найти хороших специалистов, если есть кому их искать и грамотно собеседовать.
А не IT-бизнес для своих целей скорее пойдет в какую-нибудь веб-студию и закажет сайт/магазин/портал.
PHP vs Java в web:
Java держит ресурсы в памяти, PHP при каждом обращении создает новое подключение к ресурсу (к БД) — так кто держит ресурсы в памяти у Java? А держат их сервера приложений типа JBos, WildFly, WebSphere, и даже пулы коннектов к БД создаются в них. У Spring Boot под капотом Tomcat. Так на PHP сейчас есть Swoole, Workerman и т.д. и т.п. Ну а если самому захотелось открыть web socket, так PHP и такую возможность дает. Вот для чего Java или Scala подходят, это написание полноценных приложений типа БД, брокера очередей и т.п., для этого PHP не приспособлен конечно. Основная проблема у Java и Scala, как впрочем и для других языков, если хочешь качественный код, то нужен хороший/дорогой специалист, а гавн-кода и на этих языках хватает, вы уж поверьте.
PHP vs NodeJS в web:
NodeJS — программная платформа, основанная на движке V8 (транслирующем JavaScript в машинный код). Это вообще не язык программирования, а кастыль транслирующий JS в машинный код. На моей прошлой работе решили попробывать NodeJS вместо PHP, так плеваться потом начали, даже при использовании async/await система все равно не справлялась с нагрузкой, пользователи подвисали на достаточно долгое время прежде чем получить результаты работы, элементарно залогиниться в системе долго не могли (например).
PHP vs Rust:
Rust это вообще системный язык программирования, как их можно сравнивать то!?
PHP vs Go:
Простые сервисы писать на Go однозначно проще, но сложную логику как на нем писать не преставляю, в моем понимании если надо взять запрос и тут же выплюнуть ответ Go лучшее решение. Если не прав, пожскажите в чем, Спасибо!
Какие сделал для себя вывыоды, PHP удобен для:
1. Как связующее звено между различными системами, для организации инфраструктуры компании. Позволяет выбирать лучшие решения и пользоваться ими, а если решили использовать что то другое, то незаметно для пользователей переключиться на него.
2. Возможна организация сложной логики работы с данными. Т.е. получили данные, провели их анализ и приняли решения. Конечно и другие языки на это способны, но PHP в некоторых случаях удобнее.
3. Быстрая организация инфраструктуры проекта. Например за неделю до Нового Года менеджеру стукнуло провести необычную Новогоднюю рекламную акцию, PHP для этого на мой взгляд подходит лучше чем Java, NodeJS, Rust или даже Golang.
Тут ещё вопрос: а про чей выбор идёт речь? В марте-апреле искал новый проект, было пара вакансий в типа стартапах, предполагающих возглавить техническое создание продукту с нуля, включая набор команды. И как-то эти люди, без, судя по всему, технического бэкграунда, PHP выбрали. Одни у себя Европе по каким-то критериям, и зашли в украинский аутсорс с чёткими требованиями "нам нужна команда фуллстэков-пехепешников". Другие сначала у этого же аутсорса заказали архитектуру, включая выбор стэка, а потом "наберите нам под это команду".
Ещё выбирает не ИТ-бизнес PHP, когда решено создать новую систему и стоит вопрос выбрать PHP и сразу приступить к её созданию силами имеющейся команды, или выбрать другой стэк и то ли переобучать команду (непонятно кем и как), то ли разогнать её, оставив пару человек на поддержку "легаси", и набирать новую, погружать их в предметную область и т. п.
Тут две стороны у вопроса. Иногда ко мне обращаются за консультациями типа "какой стэк нам выбрать для такого-то продукта" и мой первый вопрос "а делать я буду?". Если нет, то могу порассуждатть "сюда нода хорошо зайдёт или гоу, сюда PHP, Python или Ruby. Сюда С#/Java, а вот эта часть выглядит как придуманная под Эрланг". Если же да, то "основной стэк PHP+Symfony"
Если же да, то «основной стэк PHP+Symfony»
Хороший стэк и работать удобно, когда есть PHPStorm + Symfony Plugin
Только на этом стэке я могу сесть и начать писать, не заглядывая каждую минуту в доки и гугл языка и экосистемы. Ну, на ноде могу, не заглядывая в доки языка.
Для меня преимущество :)
Я имею в виду для моих заказчиков, которые не ищут кто-бы им реализовал проект, а которые хотят чтобы прежде всего я рулил проектом с технической точки зрения. Для них преимущество PHP в том, что если выберут его, то я смогу им эффективно рулить не только на уровне глобальной архитектуры, но и, грубо говоря, код-ревью грамотно проводить. Грубо говоря, им не нужно будет пару Java, например, сеньоров нанимать кроме лида/архитекта (меня), достаточно одного PHP-сеньора будет.
Планируется обсудить вопрос «Как и кем формируется рынок PHP-разработки, кто по факту решает, на чем писать и переписывать проекты.», думаю он во многом прояснит ситуацию.
Холивар удался? Стоит писать? :)
Вот. Так что советую дождаться разметки к видео и посмотреть хотя бы часть — кусками, какие заинтересуют.
Со слов докладчика, Swoole бьет и NodeJS и Golang.
А что будет когда PHP 8 выйдет (разработчики swoole рекомендуют использовать чем новее версия PHP, тем лучше)!? При этом разработчики swoole позиционируют себя как пришли они надолго и всерьез, что у них сейчас работает стабильная команда разработчиков.
Так же есть фреймворк swoft у которого под капотом swoole, а спереди стоит сервер на Golang у которого время отклика лучшее. Так же у swoft уже есть реализованный пулл коннектов к БД (для долго живущего приложения это очень важно!), есть своя ORM. Swoole (соответственно и Swoft) tcp подключение могут обрабатывать и WebSocket подключение обрабатывать. Так же coroutines есть с каналами (как у Golang), плохо развито на данный момент, но ведь это только начало.
Вот и заставляет задуматься, бросать этот язык или нет. Честно, я в замешательстве, ведь сейчас так много говорят о phyton, nodejs, scala, java, haskell и т.п.! Помогите!!!
Посмотрите hyperf фреймворк, там уже с бриджами под PSR.
Хотя гавн-код можно писать и в Java (например), уверяю Вас.
JS (прототипное ООП) так там вообще бардак еще творится, классы например поддерживаются начиная с ES6, как написать сложное приложение в более ранних стандартах без «лапшы» или кастылей, даже не знаю, может кастыль под названием Buble использовать?
Phyton — мало знаком с данным языком, брат на нем пишет и он ему нравится, язык сейчас конечно популярен, но если посмотрите бэнчмарки которые предоставил ниже, то он проигрывает PHP.
Другой вопрос в том, что знание и опыт и Java и NodeJS очень помогло мне в жизни, без данного опыта думаю было бы сложновато, хотя сейчас очень много литературы стало доступно по написанию хорошего кода вне зависимости от языка программирования.
По поводу ЗП — очень скользкая тема, даже боюсь ее трогать.
Не знаю, «чуйка» наверное, что пока отказываться от PHP не стоит, но развиваться, изучать раличные подходы в программировании надо всегда! Знать основы, как что работает так же надо. Почему специалисты PHP часто ценятся не высоко, многие просто даже не знают что такое стек, что такое очередь, хэш таблицы считают что это просто массивы PHP, но это больше в силу того, что цена входа низкая.
Ну а люди, кто развивает PHP, явно не глупые!!! У меня сложилось такое впечатление, что они владеют другими языками и в PHP стараются взять только лучшее, избегая ошибок других языков.
по данным компании NewHR, в июне 2019 года средняя зарплата PHP-программиста уровня middle в Москве составила 160–180 тыс. рублей. Senior’ы зарабатывают в среднем 180–240 тыс. рублей. Зарплата team lead’а составляет от 240 тыс. рублей.
По хорошему все языки приблизительно в одном русле, плюс — минус, Scala наверное только выделяется.
PS: в DevOps вообще надо идти.
По зарплатам имеется ввиду, я думаю, Ops.
Хотя гавн-код можно писать и вЯ же говорю, дело не в технической области. У РНР крайне низкий авторитет в IT, чуть выше, чем у 1С.
Думаю, зарплаты в вашем примере из-за того, что раньше уже наделали много на РНР и с этим надо работать, а притока хороших специалистов нет из-за плохого мнения о ЯП, дефицит поднадувает зарплаты. Но я помню другие исследования, в т.ч. тут, что РНР всё-таки менее оплачиваемый. И однозначно это не ЯП для карьерного выбора.
И опять кмк, намного более важным является участие в
Да и треды сейчас такие, что только Python стабильно растет, но его я могу порекомендовать кому-то только как начало карьеры (не в обиду для спецов в этом ЯП).
upd
github.com/the-benchmarker/web-frameworks
Лично для меня у пыхи есть ещё одно неоспоримое преимущество — использование его в том виде, как изначально и задумывали, в виде HTML на стероидах.
Вот совсем недавно была ситуация — есть демон, который работает по специфичному протоколу с прогой и хранит от неё данные. При этом сама прога редактировать часть данных не умеет, поэтому нужно редактировать их через демона. Демон торчит наружу REST-APIшкой об одном эндпоинте для GET/PUT/PATCH по айдишнику без авторизации.
Стало быть, чтобы не дёргать постоянно curl, хочется вебморду. Начал прикидывать.
Реакт/Вуе/Ангуляр? Это надо демона голой жопой в интернет выставить либо только с локалки им рулить, не подходит.
Писать в демона на расте вебморду прямо внутрь? Вариант, но трудозатрат дофига, а выхлоп, как мы уже помним, одноразовый.
Писать ещё одного демона на расте/го/ноде, который умеет парсить шаблоны и подставлять в них результаты запроса к первому демону? Так то почти ПХП и получится.
В итоге за пару часов набацал в виме на бутстрапе и пхп вебморду, которой не противно пользоваться вроде как. Но теперь постоянно вопрос гложит — вот я-то понаписал прям как в начале нулевых, в начале файла проверка переменных запроса и запрос к демону, дальше выдача веб-страницы.
А как такое в современном мире пишется правильно? Не троллинга ради, а самообразования для — какие есть альтернативы, чтобы быстро и просто написать и развернуть веб-интерфейс к RESTful АПИ безо всякой логики внутри него? Может быть, можно уже это сделать проще и быстрее, чем на пыхе?
Зачем писать на PHP в 2020? Холиварим в режиме интерактивного подкаста на Youtube в этот четверг