Ответы пользователя по тегу Проектирование программного обеспечения
  • Как понять Golang JavaScript\PHP разработчику?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Go - это не про ФП. ФП - это всякие Erlang, Elixir, Haskell и т.д.

    Что касается интерфейсов - да, это то место, которое вам стоит хорошенько разобрать. Что бы понять зачем и как их использовать: представьте, что пишите на том же php, только каждый класс помечен как final и у вас грубо говоря "нет массивов для сборных солянок" в стиле ['a'=>5,'b'=>['c']] (да, в go это можно сделать, но при возможности лучше все же не делать).

    Конкретно в вашем случае - под каждого вендора заведите отдельный тип (если нужно конечно). Что содержит вендор? Имя и адрес - создайте интерфейс который будет содержать два геттера для имени и для адреса. В местах использования указываете этот интерфейс и выполняйте с ним необходимые действия.
    Ответ написан
  • Как разбить транзакцию по микросервисам сохранив консистентность данных?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Если возникла проблема - очень большая вероятность того, что разделение на микросервисы было не корректным и стоит вернуться к монолиту.

    Что касается распределенных транзакций. Как минимум можно пытаться повторять запросы N раз, в противном случае откатывать на каждом из сервисов.

    Как вариант можно использовать всякие kafka для хранения истории сообщений с целью дальнейшего восстановления неотрботавших транзакций.

    Нужно продусмотреть причины отката транзакций каждым из сервисов, например на счету нет денег - транзакция оплаты невозможна.

    Правильного варианта не существует. Все зависит от проекта
    Ответ написан
  • Начальное проектирование программ на Golang: нужен ли вам UML или что вы используете для наведения порядка?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Вы пытаетесь скрестить ежа с ужом.

    UML схемы действительно полезны, когда сразу уместить в голове сложную систему взаимодействия не получается. Например у вас есть 5 обособленных внешних систем со своими состояниями и вам необходимо спроектировать api между каждой из них. UML в этой ситуации вас очень выручит. Если в основном будете использовать sequence диаграммы - рекомендую смотреть в сторону PlantUML. Во всяких VisualParadigm на переделки (а они будут) вы потратите кучу времени, а с текстом в PlantUML это на порядки проще.

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

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

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    1. С этого надо начинать. Обновлять автоматизированно. По бд - это миграции.

    2. Если вы обеспечиваете хостинг и поддержку, разделить по тазикам будет хорошей илеей.
    Ответ написан
  • В чем преимущества ValueObject и DTO?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Преимущество VO в том, что это значения, которые уже проверены на границы и тип. В вашем примере валидация происходит в геттере - это мягко говоря бессмысленно. Вот вам пример
    class UUID
    {
        /** @var string */
        private $value;
    
        /**
         * @param string $value
         */
        public function __construct(string $value)
        {
            if (!preg_match('/^[\da-f]{32}$/', $value)) {
                throw new \InvalidArgumentException(
                    sprintf('Argument "$value" must be correct UUID, actual value: "%s"', $value)
                );
            }
            
            $this->value = $value;
        }
    
        /**
         * @return string
         */
        public function getValue(): string
        {
            return $this->value;
        }
    }


    Дальше в коде вам достаточно делать type hinting на тип VO и все, значение будет корректным. От рефлексии, или runkit вы все равно защититься не сможете.

    -- --

    DTO - это штука, для удобного транспорта данных, между разными частями системы. Например у вас есть метод, который на вход принимает 20+ аргументов (например регистрация), вызывать такое кодло вероятно будет не удобно, но собрав dto вы можете его передать одним аргументом и рассчитывать на то, что данные переданы с правильными типами. Граничные же значения придется проверить, так как в задачу "транспорта" не входит контроль правильности данных между системами, что его используют.

    но вот внутри в тех частях логика которых фиксирована и не может меняться - вполне подходит массив ключ-значение

    Использование KV в контексте DTO/VO - дико хреновая практика, в очень редких кейсах ее использование оправдывает себя. Дело в том, что массив - это набор произвольных данных. Что бы писать надежный код - вам придется на каждом этапе делать проверку правильности этого массива. Что это за проверки?
    * все нужные ключи существуют
    * все значения по этим ключам правильных типов
    * массив не содержит левых данных

    как реализовать на php типизированую коллекцию типа как в СИ

    class MyTypedCollection implements \Countable, \IteratorAggregate, \ArrayAccess
    ...
    Ответ написан
  • Правильно ли я понимаю принцип модульной архитектуры приложения?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Зависит от проекта конечно, но в понятие модуль обычно вкладывается что-то очень больше и независимое, например система управления пользователями, система управления платежами и т.д. В терминологии Symfony - это называется Bundle.

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

    А если мне нужно написать модуль регистрации пользователя? Как здесь быть?

    Не использовать парадигму, которая вам не подходит. Почитайте про SOA. Если в двух словах: сервис - это некий набор бизнес логики, который умеет обычно всего одно действие, но делает его качественно.
    Для сервиса регистрации данных вывод шаблона не нужен. Все, что он должен уметь: проверить входные данные для регистрации, сохранить их и оповестить свои зависимости об этом (например сервис отправки почты).

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

    З.Ы. Битрикс и "качественная архитектура" - это вещи не совместимые))
    Ответ написан
  • Как правильно организовать обработку данных в экшене контроллера?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    По хорошему валидацию должен делать реквест

    Неа. Request - это просто набор данных.

    Тоесть выходит 2 запроса, 1 - через валидаторы фреймворка, а 2 - когда в контроллере или сервисе достаем эту-же запись для дальнейшей работы.

    Вы пытаетесь найти единственное правильно место для проверок. По хорошему проверки должны быть во всех методах, даже в приватных. Что-то не так - бросайте исключение. Эта практика на первый взгляд жуткая, но она вас обезопасит от огромнейшего количества ошибок.

    Опережая ваш вопрос: "это ж сколько дублировать проверки придется?". Да, много, но поверьте, оно стоит того.

    В добавок к этому иногда логика может быть достаточно замысловатой

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

    Мне не очень нравится идея выносить куски логики в реквест

    Мысль здравая.

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

    Не нарушайте SRP.

    <?php
    
    namespace Vendor\Project\AppBundle\Controller;
    
    use KoKoKo\assert\Assert;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\HttpFoundation\JsonResponse;
    use Vendor\Project\Path\Authorization\UserNotFoundException;
    use Vendor\Project\Path\Authorization\InvalidCredentialsException;
    
    class LoginController extends Controller
    {
        /**
         * @Route("/login", name="login")
         * @Method({"POST"})
         * @param Request $request
         * @return JsonResponse
         */
        public function loginAction(Request $request) : JsonResponse
        {
            try {
                $login = $request->request->get('login');
                $pass  = $request->request->get('pass');
    
                Assert::assert($login, 'login')->string()->notEmpty()->match('/^[a-z\d]{3,32}$/i');
                Assert::assert($pass, 'pass')->string()->notEmpty()->lengthBetween(6, 32);
            } catch (\Throwable $exception) {
                return new JsonResponse($exception->getMessage(), JsonResponse::HTTP_BAD_REQUEST);
            }
    
            try {
                $user = $this->get('UserAuthorizator')->authorize($login, $pass);
    
                return new JsonResponse($user->getId());
            } catch (UserNotFoundException $exception) {
                return new JsonResponse('User not found', JsonResponse::HTTP_BAD_REQUEST);
            } catch (InvalidCredentialsException $exception) {
                return new JsonResponse('Invalid login or path', JsonResponse::HTTP_BAD_REQUEST);
            } catch (\Throwable $exception) {
                $this->get('logger')->error($exception->getMessage(), ['exception' => $exception]);
    
                return new JsonResponse('Something went wrnog :((', JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
            }
        }
    }
    Ответ написан
  • Если ли смысл в ОРМ для моего случая?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Есть ли порог когда использование ОРМ становится неоправданым/оправданым? Где он?

    Чаще всего он становится неоправданным из-за слишком высоких накладных расходов.
    Пример:
    User MANY_TO_MANY Group
    User MANY_TO_MANY Category

    Нужно найти все категории, по заданному списку групп пользователей. В случае Doctrine ORM вам придется загрузить список груп, дальше циклом пройтись по всем пользователям (если загрузка ленивая - это куча запросов), дальше уже по всем пользователям пробежаться и вытянуть все категории, плюс убрать дубликаты. Тут лучше plain sql, или query builder, как вам больше нравится.

    В случае же когда, накладные расходы не высоки И ORM будет более удобной - стоит юзать ORM. Например: у вас уже есть загруженный пользователь, хотите достать все его группы. В случае Doctrine ORM это может выглядеть как: $user->getGroups().
    Ответ написан
  • Проект со сложной логикой на Symfony – как проектировать? Примеры?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Как хранить бизнес-логику чтобы модели не превратились в монстров из десятков тысяч строк?

    Тут не совсем модели. Entity - это просто объект данных, умеет хранить их в себе и бросать исключения, если не правильные данные вставляете, все. Repository - умеет работать со своим Entity И БД.

    БЛ находится в классах сервисах.

    Читал про Command Bus где, если правильно понял, на каждое действие в системе – свой класс?

    Вообще под каждое дествие - не обязательно, тут нужно руководствоваться здравым смыслом.

    Как их организуете (их тогда будут сотни)?

    Иерархически. Путь к классу должен быть "понимаем".

    Есть ли смысл выносить каждую доменную модель в модуль/микросервис, хранить всю связанную логику где-то там внутри, а с остальными общаться по внешнему API?

    Только в тому случае, если вы уже делали такие же проекты и в точности знаете максимально точно границы каждого домена. Иначе - не стоит. Делайте монолитную систему, а разделение на микросервисы - только по факту необходимости.

    За ответы в клиентскую часть – отдельный сервис-фронтенд?

    Если в "сервис" вы вкладываете понятие простого класса, умеющего форматировать ответы вашего проекта - мысль здравая.
    Если ответы будут асинхронными (от сервера к другому) - имеет смысл выностить в отдельный клиентский класс.

    Каков оверхед?

    Ничтожный.

    Используют ли такое на практике?

    Да

    Какие подводные камни?

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

    Как не превратить кидание/получение событий типа PostBeforeEdit/PostBeforeEditHandler в "callback hell"?

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

    Функционал "PostBeforeEdit/PostBeforeEditHandler" часто дешевле и проще вынести в сервис, но опять же руководствуйтесь здравым смыслом.

    ACL Где храните указанную логику?

    Если ACL будет не тривиальный - готовьте себя к тому, что он будет размазан по уровню контроллеров.

    Какие структуры для описанного выше – best practice?

    Если есть возможность привести к одноуровневому виду - сделайте. Если с точки зрения бизнеса может потребоваться иерархическая (не фиксированной вложенности) ACL - до последнего убеждайте, что это плохая идея, не повторяйте чужих ошибок.

    В моём понимании это выглядит как куча трёхмерных кубов доступа "crud – group – entity – field", как это сделать более плоским пока только одна идея – делать кучу таблиц many-to-many.

    Гибкая настройка вплоть до каждого поля 90% что не нужна. Если можно свести к понятию скопов прав - сделайте это.
    Структуру можно предлагать только зная ваш проект.

    Версионирование. Как вы версионируете подобные проекты?

    Semver.

    А если нужна "N-1" рабочая версия на продакшене?

    Значит на прод попадает ваша версия с тегом "N-1"))

    Есть ли смысл разделять версии в рамках единой кодовой базы проекта и как (неймспейсы, конфиг, модуль, что-то ещё)?

    Храните яйца в отдельных корзинках. Если модуль развивается полностью отдельно и может быть вынесен как зависимость проекта в vendor - делайте.

    И, самое главное – как всё это совместить?

    • РУКОВОДСТВУЙТЕСЬ ЗДРАВЫМ СМЫСЛОМ
    • Принимаете жесткие соглашения по правилам написания кода, например такие
    • Постарайтесь убедить бизнес в том, что без покрытия кода автотестами будет дороже, нестабильней и дольше. + Пишите тесты. Если объем тестов в 4 раза больше кода, который они тестируют - это норм. У меня бывали случаи, когда для критичного функционала тестов было в ~16 раз больше, чем кода.
    • Жесткие, обязательные кодревью.
    • Если задача крупная - декомпозируйте ее.
    • Технический долг - возвращайте обязательно И как можно скорее.
    • Перед тем как писать код для работы с внешним сервисом - имеет смысл написать его эмулятор.
    • Спешите только в случае серьезных проблем на проде)). Фичи "на вчера" отличаются от фич "на потом" только приоритетом выполнения, более ничем.
    Ответ написан
  • Построение многопользовательского сайта - где найти основы основ?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Как устроена простейшая авторизация?

    Проверяется открытый(логин) и закрытый(пароль) ключи. Если они соответствуют - в сессии пользователя сохраняется идентификация этого пользователя.

    Как пользователь находится в сети

    Он отправляет к вам HTTP запрос, так и находится.

    отличается от других пользователей

    Для каждого пользователя генерируется уникальное значение сессии и сохраняется в cookie.

    как инициируются и как разделены действия отдельных пользователей на сайте

    Инициируются пользователем с помощью HTTP запроса. Разделены за счет разных идентификаторов сессий.

    не зарегистрированных и зарегистрированных с различными правами привилегий?

    Проектируется и реализуется система контроля доступа. Есть 4 основных типа реализации:
    1. Проверка в сессии идентификации пользователя, если она есть - авторизирован.
    2. ACL
    3. RBAC
    4. С помощью внешних сервисов, например LDAP, OAuth,...

    Если кто видел толковые, исчерпывающие мануалы по данному вопросу - просьба поделиться.

    Любой учебник по php от 700 страниц.
    Ответ написан
  • В каком виде передать данные?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Про существование $_POST, $_GET,... после того как вы собрали Request в принципе стоит забыть.
    Ответ написан
  • Как реализовать обратную совместимость пользовательских данных в программе?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Храните набор миграция от старых версий до последней. При сохранении старого проекта - выполняйте обязательное приведение к последнему формату. При открытии проекта - тоже самое
    Ответ написан
  • Десктопные приложения на интерпретируемых языках. Как это работает?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Можно ли так сделать

    Можно

    как сделать приложение на подобном языке, которое не будет требовать от конечного юзера установки интерпретатора со всеми модулями и остальным окружением?

    Интерпретируемые языки требуют наличия интерпретатора. Ваше приложение может быть со встроенным интерпретатором (обычно игры так делают).

    Десктопные приложения на интерпретируемых языках. Как это работает?

    Работает так как запрограммировано)). Откройте /etc/init.d/cron вот вам пример программулины на баше, если запустите этот файл - увидите как она работает.

    Что касается питона - в чем проблема, поищите приложения, написанные на нем и почитайте код.
    Ответ написан
  • Что скажете о моей реализации логирования?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Магические текстовки нужно заменить на константы.

    В итоге я могу хоть 800к логов загнать в бд без большой нагрузки на неё.

    Круть. За какой период времени? Какое железо? Нагрузка и на редис и на БД постоянная?

    Чем ваш логгер лучше https://github.com/Sirupsen/logrus ?

    -- --

    Посмотрел статью на хабре, увы там комментировать возможности не имею.

    Кровь из глаз((

    0. Посмотрите logrus и переименуйте статью на "еще один логгер". Ваш логгер не гибкий, примите за исходную.

    1.
    Данные в редисе

    вы правда думаете кто-то будет это читать?))

    2.
    Я долго бился голов об клавиатуру искал багу в коде...

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

    3.
    1 000 000 записей в мускуль добавилось за 25мл.сек

    Что-то слабо верится(( Сколько выполняется запись просто из файла, такого же объема данных. Интересует сравнение времени.

    4.
    правда в редис эти данные писались около 6 минут

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

    5.
    конфиг

    Кровь из глаз, конфиги если уже json делаете - хотя бы делайте человеко понятными, не используйте массивы там, где должны быть объекты!!!

    6.
    библиотека

    То, что вы делаете в init - это как минимум богомерзко((( Ваш логгер абсолютно не управляем. Его настройка приколочена 100-тыми говздями и заварена арматурой, как у вас вообще возникла мысль говорить о гибкости?))

    7.
    //Путь к файлу с конфигами
    config_file string = "/home/v-smerti/localhost/api/src/microService/config/log.json"

    1440x900_484850_%5Bwww.ArtFile.ru%5D.jpg

    8.
    демон

    Вы хоть сами смотрели?))

    9. Вы нигде не обрабатываете ошибку отправки почты, это вообще как? Если связь с сервером нарушено - логов не будет?))

    10. SQL:
    `id` int(11) NOT NULL AUTO_INCREMENT,
    Я надеюсь вы осмысленно допускаете отрицательные id.

    11. SQL:
    `type` text NOT NULL,
    эт пи*дец.

    12. SQL:
    KEY `type` (`type`(191))
    Если не секрет - почему не 192?
    Ответ написан
  • Что нужно знать для создания приложения для видеосвязи?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Поисковые слова: video streaming, wowza, red5, ffmpeg-server, rtmp

    Знать нужно много, что именно - зависит от конкретного ТЗ и выбранных технологий для его реализации
    Ответ написан