• Как вы разрабатываете свои приложения?

    @xfg
    Я TDD использую. Без тестов совершенно не понимаю сделал задачу или еще нет и все вокруг кажется недоделанным. Здесь же поделил крупную задачу на ряд небольших, написал тесты, написал код и четко чувствуешь момент, когда можно переходить к следующей задаче. Но я не в релизе, поэтому я плохой пример, так как делаю потому что интересно сделать бесконечно масштабируемое full-websocket приложение с использованием 3d и слоистой архитектурой из всех этих умных книг по объектно-ориентированному программированию, а не потому что жажду миллиард. За это время уже сбился со счета сколько сменил фреймворков, баз данных и даже поменял язык в итоге. За это время успел родиться AngularJS (тот что 1.x) и умереть, а я все пишу :)
    Ответ написан
  • Как лучше обращаться с параметрами метода?

    @xfg
    Аргументировать довольно просто и даже сослаться на конкретные абзацы из книги Implementing domain-driven design, а именно, то что автор пишет про сервисы. Автор книги довольно четко рассказывает почему сервисы не должны обладать состоянием.

    У вас классический сервис и у вас проблема, только лишь потому, что вы впринципе неправильно подошли к проектированию вашего сервиса.

    В первую очередь вам нужно спроектировать интерфейс CalculatorInterface

    interface CalculatorInterface {
      public function calculate($products, $city);
    }


    Далее вам нужно создать две реализации этого интерфейса CdekCalculator и EmsCalculator

    class CdekCalculator implements CalculatorInterface {
      public function calculate($products, $city) {
         // реализация расчета через cdek
      }
    }
    
    class EmsCalculator implements CalculatorInterface {
      public function calculate($products, $city) {
        // реализация расчета через ems
      }
    }


    И теперь ваш класс доставки будет выглядеть таким образом

    class Delivery {
      private $calculator;
    
      public function __construct(CalculatorInterface $calculator) {
         $this->calculator = $calculator;
      }
      public function calculate($products, $city) {
        //тут ещё какая-то логика, которая определяет условия доставки, если она вообще возможна
        return $this->calculator->calculate($products, $city);
      }
    }


    Предмета спора больше нет. Вы просто передаете в класс Delivery нужную реализацию калькулятора. Код выше это псевдоязык, не php и вообще только базовая идея того, как это должно быть. У вас наверняка тут же возникнет вопрос, как вам тогда выводить все расчеты доставки в ваш UI. Но извините не могу написать за вас всё приложение.

    Это базовые вещи в объектно-ориентированном программировании. Называется полиморфизм подтипов. Без этих знаний невозможно писать хороший объектно-ориентированный код. Лучше оба придите к выводу, что вам срочно нужно почитать какие-нибудь книги об объектно-ориентированном программировании и начать с самых основ, что такое полиморфизм и в частности полиморфизм подтипов.

    Вообще protected/private методы и ветвления из условных конструкций - это практически всегда показатель спагетти-кода и первый звоночек, что вы пишете процедурный код используя ООП синтаксис. Вы придете к этому выводу если будете разрабатывать через TDD, а затем сможете нагуглить ваши предположения у других программистов.
    Ответ написан
  • Как вы относитесь к использованию транзакций в Laravel?

    @xfg
    Транзакции повторяют из-за дедлоков. Это такое состояние, когда одновременно выполняются 2 транзакции и первая транзакция блокирует запись, которая нужна второй транзакции, а вторая транзакция блокирует запись, которая нужна первой транзакции. В итоге ни одна из транзакций не может завершиться. Это состояние называется deadlock. В этом случае одна из транзакций должна откатиться, чтобы другая имела возможность успешно завершиться. Так вот, эта транзакция, которую откатили попробует еще 3 раза выполниться, прежде чем окончательно вылетит с исключением.

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

    Вообще на более продвинутом уровне принято сохранять не более одной сущности (объекта) внутри одной транзакции. Таким образом вы получаете худые транзакции и отсутствие дедлоков. Производительность вашего приложения возрастет, но вместе с тем, вы получите проблему атомарного сохранения нескольких сущностей (объектов) в рамках одной бизнес-операции как это происходит в микросервисной архитектуре. Проблему можно решить использованием шаблона saga, но в целом это уже совершенно иной уровень знаний и вникать в это наверное не стоит, если вы делаете что-то небольшое.
    Ответ написан
  • Правильно ли использовать Web Socket PHP для онлайн игры на unity?

    @xfg
    Да, экшены так и делают. Сервер может отправлять около 20 снепшотов игрового состояния на клиент, то есть приблизительно каждые 50 миллисекунд. Чтобы изображение не дергалось отображение игрового состояния смешают назад в прошлое скажем на 50 миллисекунд, а клиент таким образом имеет возможность интерполировать перемещение игроков и анимации между текущим и предыдущим состоянием. Соответственно и сервер должен рассчитывать ввод пользователя в прошлом учитывая это смещение на 50 миллисекунд, учитывая время на путешествие пакета от клиента до сервера и возможно что-то еще, чтобы сделать стрельбу или маханием мечем максимально точным.

    Для пошаговой игры это избыточно. Там достаточно просто отправлять новое состояние с сервера только в ответ на ввод пользователя.

    Другой вопрос стоит ли для этого использовать websocket php, когда насколько мне известно unity итак предоставляет возможности для организации мультиплеера.
    Ответ написан
  • Зачем использовать реляционные субд для этой ситуации?

    @xfg
    Если комментариев к статье неограниченное количество, то хранить данные таким образом - неудачная идея. В mongodb документ по умолчанию не может превышать более 16 mb, но даже при отсутствии подобного ограничения, это привело бы к единовременному считыванию большого объема данных в память. В таком случае данные следует моделировать также, как и в РСУБД.

    Есть CAP-теорема и выбирать тип базы данных нужно исходя из того, какие 2 из 3 свойств для вас важнее. РСУБД дают согласованность и доступность (CA), но жертвуют возможностью разделения такой базы, nosql решения идут другим путем и жертвуют согласованностью в пользу доступности и устойчивости к разделению (AP). Согласованность данных в таких системах достигается при помощи шаблона Saga вместо ACID.

    В целом NoSQL это про проекты данные которых не помещаются в рамках одного сервера, а не про то, каким образом моделировать эти данные. Вложенные документы в mongodb - это скорее следствие её архитектуры, которое используют как маркетинговый ход, чем решение, которое будет использоваться повсеместно. В подавляющем большинстве случаев документы будут ссылаться друг на друга, точно также как это происходит и в РСУБД.

    Для проекта с крохотными объемами данных логичнее выбирать РСУБД и тем самым значительно упростить себе жизнь. Но в целом как используют NoSQL, шардируют данные и как достигают согласованности данных при отсутствии ACID знать желательно, хотя бы в общих чертах.

    Самое ужасное, это выбрать базу данных и использовать её неправильно, как пример документа с вложенными комментариями при условии их бесконечности в mongodb. Пишем скрипт генерирующий триллион комментариев к статье, а затем просим нам выдать эту статью и сайт уходит в офф. Хорошо что в mongodb есть защита от дурака.
    Ответ написан
  • Уступает ли SPA-приложение традиционным сайтам с точки зрения SEO?

    @xfg
    Современные поисковые системы умеют индексировать javascript сайты. Можно почитать более подробно об эксперименте. SSR и прочие хаки более не требуются.
    Ответ написан
  • Зачем нужен Gulp/Webpack?

    @xfg
    Webpack - это сборщик модулей, который позволяет писать модульный код, где каждый модуль имеет возможность импортировать различные зависимости, начиная от других модулей, до css стилей и изображений. На главной странице сайта даже есть пример, вы подаете на вход "модули с зависимостями", а на выходе получаете то, что сможет работать в браузере.

    Webpack - это просто крайняя точка развития идеи самовызывающихся функций, с помощью которых пытались решить проблему загрязнения глобальной области видимости, когда каждый скрипт подключенный к странице мог свободно обращаться к любым переменным и функциям из других скриптов, что приводило к различным проблемам, вроде случайного переопределения переменных, неявных зависимостей и необходимости строгой последовательности подключения скриптов. Эта идея затем переросла в requirejs, затем в browserify и вот в наши дни - webpack/rollup/parcel. Webpack анализирует зависимости, которые вы подключаете в вашем коде и собирает из этого конечный bundle, который сможет работать в браузере.

    Gulp - это менеджер задач для автоматизации различных рутинных операций, таких как минификация, тестирование, объединение файлов и тому подобное. Gulp в отличии от Webpack никак не анализирует ваш код. Он вообще ничего не делает и по-сути своей бесполезен.

    Gulp - это набор оберток-плагинов над различными утилитами. Это ведет к ряду проблем - плагины перестают поддерживаться разработчиками, плагины ломаются при очередном мажорном релизе Gulp, плагины не позволяют вам использовать новую версию утилиты, до тех пор, пока разработчик плагина не выпустит новую версию плагина совместимую с новой версией утилиты. Инструмент, который изначально возник, чтобы помочь решать возникающие проблемы, сам превратился в проблему. Это всё привело к тому, что от Gulp стали отказываться в пользу чистых утилит, которые теперь запускают через npm скрипты. Если посмотрите любые популярные open-source библиотеки, например bootstrap, то сможете заметить, что в 3 версии был Gulp, в 4 версии его не стало. Использовать Gulp сегодня не имеет смысла. Идея Gulp/Grunt - умерла, так как идея оберток-плагинов не принесла ничего, кроме дополнительных проблем.
    Ответ написан
  • Почему некоторые люди утврерждают что плохо использовать jQuery?

    @xfg
    jQuery - это про императивное программирование. jQuery - это про то "как" манипулировать dom элементами. Итог - смесь dom и бизнес-логики. Это невозможно разделить.

    Angular - это про декларативное программирование. В Angular мы не говорим "как", мы говорим "что" мы хотим сделать с dom и он делает это. Что позволяет отделить бизнес-логику от манипуляций с dom.

    Соответственно вся эта история не про jquery vs vanilla, а про императивное vs декларативное программирование для работы с dom. Большинство этого не понимает и спорят о jquery vs vanilla, хотя всё это одно и то же, до тех пор пока у вас не появляется какая-то штука, которая меняет парадигму работы с dom с императивного на декларативный. Внутри этой штуки можно использовать хоть vanilla, хоть jquery. Без этой штуки - у вас каша, хоть с vanilla, хоть с jquery.

    Собственно об этом написано в википедии:

    AngularJS is built on the belief that declarative programming should be used to create user interfaces and connect software components, while imperative programming is better suited to defining an application's business logic.


    Остальные фреймворки про то же самое. Это и стало причиной стремительного роста популярности javascript фреймворков.
    Ответ написан
  • Бросать исключение или возвращать коды ошибок/успеха? Является ли исключением то, что метод не может выполнить свою задачу?

    @xfg
    Исключения и есть современный способ реакции программы на ошибки, которые приводят к бессмысленности дальнейшего её выполнения. Соответственно, если у пользователя недостаточно средств для перевода, дальнейшее выполнение программы не имеет смысла, необходимо прервать её выполнение выбросив исключение. Когда этого механизма не существовало использовали коды ошибок.

    Вы также всегда имеете возможность посмотреть популярные open-source решения и убедиться какой точки зрения придерживается сообщество.
    Ответ написан
  • "Удаление" агрегата в DDD?

    @xfg
    Идея у вас верная, только вместо защищенного onDelete, делают обычный публичный метод remove, который ничего не делает, а просто выбрасывает доменное событие. Этот метод как и все остальные просто вызывается внутри application сервиса, а затем удаляется из репозитория.


    $catalog->remove();
    $repository->remove($catalog);


    Чтобы была атомарность, нужно события выбрасывать после того, как изменения были сделаны в базе данных. Само событие нужно также в базу сохранять, в одной транзакции с созданием/обновлением/удалением данных об агрегате. Какая-то штука затем должна удалять обработанные события. Какая-то штука должна проталкивать события из базы, которые почему-то там уж слишком долго живут - подписчикам. Обычно происходит при сбое, когда в базу событие записалось, а подписчикам не отправилось.

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

    Поэтому я бы попробовал Saga Orchestration, это такая штука, в которой пошагово описывается какие сервисы нужно вызвать, чтобы успешно завершить бизнес-операцию, а также какие действия нужно выполнить на каждом шаге в случае сбоя одного из сервисов участвующих в бизнес-операции для того, чтобы вернуть всю систему в консистентное состояние.
    Ответ написан
  • В чем разница между 'фабричным методом' и 'простой фабрикой'?

    @xfg
    Фабричный метод

    Фабричный метод позволяет избавить клиента от необходимости знать о процессе создания конкретных объектов. Клиент зависит только от интерфейса фабричного метода. Клиент используя фабричный метод может создавать и работать с любым объектом реализующим интерфейс IMobile.

    Простая фабрика

    Строго говоря в GoF нет такого паттерна как "простая фабрика". Но тем не менее это откуда-то пошло и подобный кусок кода стали называть "простой фабрикой", об этом есть упоминание в википедии и что в это можно инкапсулировать сложный процесс создания объекта, например когда он зависит от настроек в конфигурационном файле или от пользовательского ввода.

    Я не вижу паттерн в простой фабрике. Я вижу лапшу из операторов ветвления. Вместо этого пользовательский ввод или настройки конфига можно напрямую без свалки из операторов ветвления сконвертировать в имя требуемого класса фабричного метода и уже его объект передавать клиенту таким образом избавившись от спагетти-кода в виде "простой фабрики".

    Невозможно сказать кто придумал "простую фабрику" и зачем. То, что в ней написано, это спагетти-код. То, что в ней написано, можно решить более элегантным способом без свалки из операторов ветвления.
    Ответ написан
  • Как изучать node js в 2019м?

    @xfg
    Node.js не изучают. Это не язык. Здесь не учат как определить переменную или вызвать функцию. Это набор модулей, которые рассматривают как пользовательские javascript объекты. Открывают api документацию по модулю, смотрят, что делают его методы и используют в своей программе если это требуется.

    Для работы с платформой достаточно прочитать руководство.

    Лучше переключить своё внимание на английский язык.
    Ответ написан
  • Самый простой способ реализовать real time MySQL (без firebase!) базу данных для маленького приложения?

    @xfg
    Базу данных можно выбрать любую. Она не играет роли в realtime приложениях. Позвольте вам немного объяснить. Для передачи данных между клиентом и сервером в браузере существует всего два протокола. HTTP и Websocket. Firebase не магия и также использует их. Если браузером не поддерживается Websocket, то firebase откатывается на HTTP. Используя общераспространенный подход к разработке на PHP у вас не получится использовать websocket протокол поскольку типичные PHP приложения не живут дольше 1 запроса. Соответственно да, в таком варианте остается только ajax. Но точно также работает и firebase если в браузере нет поддержки websocket, так работает facebook, telegram и много всего остального. Они используют long-polling. Клиент отправляет запрос к скрипту на сервер, скрипт в цикле опрашивает хранилище mysql или более продвинутый вариант mysql+redis (чтобы не грузить запросами mysql) и пока данных не будет, цикл так и продолжит крутиться, для клиента это просто выглядит как повисший запрос к серверу. Как только данные появляются, они отправляются на клиент, соединение разрывается, а клиент сразу же отправляет новый запрос.

    Есть развитие этой идеи. Называется HTTP Streaming. Отличие от long-polling в том, что после отправки данных клиенту соединение не разрывается, а сервер продолжает отправлять последующие данные по этому же соединению. Соединение разрывается по таймауту. Минус в том, что прокси-сервера могут кешировать небольшие пакеты данных и данные нужно раздувать например пробелами, чтобы пакет данных достигал минимального размера и был способен пробить кеш прокси-сервера. Плюс в том, что если у вас данные для клиента появляются скажем с переодичностью раз в секунду, то не будет происходить постоянного открытия-закрытия соединения как при long-polling.

    Есть вариант, когда можно реализовать небольшую прослойку на socket.io. Ваше PHP приложение пишет данные для клиента например в redis. Приложение на socket.io подписывается на redis. Когда PHP что-то отправляет в redis, то socket.io мгновенно об этом узнает и рассылает это событие по websocket протоколу всем подключенным клиентам. Минусы. Раздуваете стек. Нет консистентности данных между записью в основное хранилище (mysql/postgre/mongo/etc) и redis. Соответственно может возникнуть ситуация, когда данные записали, но в redis событие не ушло. Поменяете местами, будет наоборот, событие есть, данных в базе нет.

    Вариантов в целом очень много. Всё это называется Comet. Вам проще всего реализовать long-polling.

    А реал-тайм база, которая умела пушить данные клиенту по tcp протоколу (но не в браузер) была и называлась она rethinkdb.com. Ныне не развивается. IP в России заблокирован. На сайт можно сходить по VPN.
    Ответ написан
  • Сложно ли создавать 3D игры, какие знания нужны для создания 3D игр?

    @xfg
    Знание линейной алгебры. Это первый курс технического вуза. Если каждую точку (представленную вектором) 3D модели умножить на одну и ту же матрицу, то все точки переместятся в другое местоположение относительно центра, но с пропорциональным сохранением расстояния между друг другом. Соответственно были вычислены матрицы преобразования, которые дают эффект перемещения, масштабирования и поворота точки в пространстве. Можно почитать о матрицах преобразований и самому порешать примеры, чтобы удостовериться, что они действительно дают нужный эффект. Далее это умножают на матрицу перспективы, таким образом создавая эффект отображения изображения в перспективе. Делают это на языке программирования GLSL. Пишут вершинный шейдер, который на вход принимает текущее положение каждой вершины, а также матрицы модели и перспективы, перемножая это всё в нужном порядке на выходе получается новое местоположение вершины. Далее результат передается в фрагментный шейдер, где каждый пиксель между этими вершинами раскрашивается в нужный цвет. Свет, тени и прочее, так же считают в фрагментном шейдере. Получившееся изображение выводится на экран.

    Можете посмотреть видео по теме https://www.youtube.com/watch?v=HLbxyPwdlgI&list=P...
    Ответ написан
  • Как отправить сообщение в вебсокет распределенной системы?

    @xfg
    Необходимо так или иначе реализовать механизм pub/sub. Например через redis. Все сервера подписываются на redis канал. Кто-то из серверов публикует сообщение в redis-канал. Все сервера получают сообщение и дальше рассылают своим вебсокет-клиентам.

    Подобную реализацию для библиотеки socket.io можно посмотреть здесь/
    Ответ написан
  • Как защитить от перезаписи данных? Например форму для редактирования открывают два пользователя, и одновременно меняют его?

    @xfg
    Данную проблему решают оптимистической блокировкой.

    Оптимистическая блокировка не ограничивает модификацию обрабатываемых данных сторонними сессиями, однако перед началом предполагаемой модификации запрашивает значение некоторого выделенного атрибута каждой из строк данных (обычно используется наименование VERSION и целочисленный тип с начальным значением 0). Перед записью модификаций в базу данных перепроверяется значение выделенного атрибута, и если оно изменилось, то транзакция откатывается или применяются различные схемы разрешения коллизий. Если значение выделенного атрибута не изменилось — производится фиксация модификаций с одновременным изменением значения выделенного атрибута (например, инкрементом) для сигнализации другим сессиям о том, что данные изменились.
    (с) Википедия

    То есть в базе заводите поле version, его вытаскиваете вместе с остальными данными. Затем при обновлении UPDATE SET ..., version = version + 1 WHERE ... AND version = version.

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

    В Yii вроде такое из коробки есть, а в laravel с ходу google ничего из официальной документации не выдал. Но можно и самому сделать.
    Ответ написан
  • Какие недочеты вы скажете по коду?

    @xfg
    Высокая цикломатическая сложность подобного кода. Бизнес-логика смешивается с DOM. Практически весь код дублируется.

    Такой код сложно поддерживать. Сложно читать. Сложно тестировать. Это действительно слабо. Всё же мы пишем для людей, а не для компьютера. Написать сложный код, очень просто. Написать простой код, очень сложно.
    Ответ написан
  • Стоит ли идти учиться в ВУЗ будущему программисту?

    @xfg
    В ВУЗ идти не стоит. Зачем эта линейная алгебра, векторы, матрицы, определители. Скачай юнити, потыкай в неё мышкой и миллиард в кармане, а деньги за обучение потрать на ассеты.
    Ответ написан
  • Что быстрее освоить новичку: javascript + node.js или javascript + php?

    @xfg
    Для новичков PHP проще. PHP вида "запрос-ответ-умер" прощает многое. Можно писать как угодно и при этом не иметь утечек памяти поскольку всё должно быть подчищено за разработчиком по завершению скрипта, а если нет, то проблема не ваша, а связующего ПО.

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

    Но многие используют Javascript на клиенте, никогда не слышали про event loop, их код бесконечно ест память и большинство об этом даже не знают, что не мешает вешать себе регалии сеньоров и мидлов. Если похожий подход то можно прямо сейчас начать писать боевой код на node.js. Как-то оно всё равно будет работать.
    Ответ написан
  • Как мне тестировать подобный код, нужно ли его тестировать вообще, если да - то как?

    @xfg
    Тестировать нужно. Необходимо проверить, что запрос из вашего приложения действительно уходит во вне и что из вне возвращается то, что ожидается. Поскольку обращаться к реальному registry внутри тестов это неверно, то необходимо поднять локальный registry и настроить npm-cli на работу с локальным registry вместо npm-registry для целей тестирования. Взять registry можно здесь, бросить .npmrc в директорию из под которой запускаются тесты с указанием вашего локального registry и теперь npm-cli из тестов будет ходить на локальный registry вместо реального.

    Это интеграционный тест, не юнит. Здесь задача протестировать границу между двумя модулями. Убедиться, что связка действительно работает.

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