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

    @EvgeniiR
    https://github.com/EvgeniiR
    Пусть статья и комментарии будут разными агрегатами. У комментариев будет свой домен.

    Можно в контексте комментариев сделать свой класс статьи( Article ). Он даже не будет сущностью:
    class Article {
      private UUID id;
      
      private CommentsRepository comments;
      
      ...
      
      public void function addComment(commentData: commentData) {
        this.coments.add(new Comment(this.id, commentData));
      }
    }


    В контексте комментариев не обязательно нужна статья - нужен только её идентификатор.
    Также в контексте комментариев вовсе не нужны данные пользователя - только его идентификатор. Идентификаторы достаточно стабильная информация чтобы не бояться их шарить.

    И да, в данном случае мы имеем дело просто с декомпозицией системы, к реальному DDD это мало отношения имеет, потому что domain-driven-design, он domain-driven за счёт того что конексты обсуждаются с бизнесом, а не придумываются разработчиком, в данном случае мы просто берём оттуда немножко терминологии потому что она уже стала довольно общей.
    Ответ написан
    2 комментария
  • Можно ли в контроллерах использовать другие контроллеры?

    @EvgeniiR
    https://github.com/EvgeniiR
    Владимир Голубь,
    Есть функция проверки пароля и логина, она берет информацию из env. Может это вообще model ? Она применяется в двух контроллерах.

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

    В JS:
    https://expressjs.com/en/guide/using-middleware.html и
    https://stackoverflow.com/questions/12921658/use-s...
    Ответ написан
    5 комментариев
  • Какая архитектура должна быть у простого апп для показа данных, полученных из json?

    @EvgeniiR
    https://github.com/EvgeniiR
    По сути функционала там - Gateway к апишке и Presenter какой-нибудь. Это можно явно и выразить. Среди кучи кучи хуков(protected onSomethingHappened()) и статических вызовов Utils сложновато проследить поток выполнения. (Utils выпилить).

    Ну и по мелочи:
    - Выпилить кучу закомментированных строк
    - Привести нейминги к единому стилю, желательно без всяких префиксов
    - Не обрабатывать одни и те же условия по нескольку раз в рамках приватных методов одного класса.
    Ответ написан
    2 комментария
  • Как правильно реализовать создание дочерних экземпляров класса?

    @EvgeniiR
    https://github.com/EvgeniiR
    Как то так
    class UserData {
        /**
         * @readonly
         * @var string
         */
        public $source;
    
        public function __construct(string $source)
        {
            $this->source = $source;
        }
    }
    
    class NoSupportingSenderFound extends \Exception {}
    class AnswerTransportError extends \Exception {}
    
    interface AnswerSender {
        public function supportSource(string $source): bool;
    
        /**
         * @throws AnswerTransportError
         */
        public function sendAnswer(string $answer): void;
    }
    
    class AnswerSenderFacade
    {
        /**
         * @var AnswerSender[]
         */
        private $senders = [];
    
        public function __construct(AnswerSender ... $answerSenders)
        {
            $this->senders = $answerSenders;
        }
    
        /**
         * @throws AnswerTransportError
         * @throws NoSupportingSenderFound
         */
        public function sendAnswer(UserData $userData, string $answer)
        {
            foreach ($this->senders as $sender) {
                if($sender->supportSource($userData->source)) {
                    $sender->sendAnswer($answer);
                    return;
                }
            }
            throw new NoSupportingSenderFound("...");
        }
    }


    В конструктор класса AnswerSenderFacade нужно передать инстансы всех реализаций интерфейса AnswerSender. В Symfony это просто делается через tagged services (Навесить тег на _instanceof AnswerSender через конфиг и в конфиге же указать что все помеченные тегом классы нужно заинжектить)

    Над названием конечно можно ещё подумать.

    p.s. в случае Симфони, правда, в конструкторе вместо ...$providers придётся использовать iterable и дополнительно проверять что пришли инстансы SenderInterface через instanceof
    Ответ написан
    4 комментария
  • Какие ресурсы прочитать или, может, книги,где описаны приемы проектирования ядра приложения?

    @EvgeniiR
    https://github.com/EvgeniiR
    Первое правило проектирования ядра приложения - избавиться от ядра приложения и делать нормальные компоненты с минимумом зависимостей.

    Второе правило - прочитать что такое God Object и потихоньку начать понимать причину избавления от ядра.

    Третье правило - пойти читать Чистую Архитектуру/Чистый код, разобраться с концептами coupling/cohesion и т.п.

    p.s. последовательная обработка данных множественными компонентами может решаться через сервисы - координаторы, через посредники(middlewares), цепочку обязанностей(Chain Of Responsibility), хоть декораторы в конце концов.
    Ответ написан
    2 комментария
  • По какому пути вы бы пошли при рефакторинге?

    @EvgeniiR
    https://github.com/EvgeniiR
    3) Сделать новый класс AdvancedBalance унаследовать от Balance и переопределить метод getBalance

    4) Сделать новый класс AdvancedBalance унаследовать от Balance и создать новый метод getAdvancedBalance,

    Ох уж эти ООП-девелоперы. Ради чего вы там наследовать собрались?
    Добавьте интерфейс NumberFormatter или BalanceFormatter, прогоняйте числа через них в нужных местах.

    молится что ничего не сломалось

    хоть тесты и есть

    Они не просто "есть", их ещё и поддерживать нужно. Пойдите по пути написания тестов на места которые могут поломаться.

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

    Расширять, а не наследовать, принцип о том чтобы менять как можно меньше кода, в идеале вообще не трогать, а вы, якобы следуя ему, предлагаете пол проекта перелопатить для использования другого интерфейса

    p.s. Гляньте, например, https://www.youtube.com/watch?v=pu0EXQvoaCc
    Ответ написан
    Комментировать
  • Как спроектировать агрегатор?

    @EvgeniiR
    https://github.com/EvgeniiR
    1. Сделать отдельную сущность - Рейтинг Организации, и иметь общую айдишку с организацией. Там уже статический конструктор инициализирующий рейтинг по количеству специализаций.
    Судя по описанию, ни имя, ни телефоны, ни адреса для расчета рейтинга не нужны, а значит смысла ложить его в ту же сущность нет.
    Думаю самый оптимальный вариант.

    2. Сущность можно при желании положить в поле в корне агрегата.
    Ответ написан
    Комментировать
  • Модульная архитектура и паттерн UnitOfWork в c#?

    @EvgeniiR
    https://github.com/EvgeniiR
    При подключении модуля ему передается Экземпляр класса UnitOfWork и , соответственно, модуль может пользоваться всеми полями (репозиториями таблиц) и обращаться к их свойствам.

    Это явное пренебрежение и нарушение Interface Segregation Principle. Не нужно так делать.
    Репозиторий и UoW не противоречат друг другу. Даёте сервису репозиторий, а под капотом у него уже могут быть любые инструменты, в т.ч. UoW.

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

    Модуль меняет схему БД? Думаю стоит первоначально попробовать пересмотреть его логику работы.

    Как правильно поступить, чтобы Ядро не подстраивалось под модули, а модули подстраивались под ядро. И именно модули использовали средства ядра для комфортной работы с базой.

    В ядре лежит реализация работы с базой, или лишь объявления необходимых интерфейсов?
    Для реализаций то есть отдельный Data Access Level.
    По хорошему ваш слой с бизнес логикой должен объявлять нужные интерфесы для работы с хранилищем, а слой доступа к хранилищу эти интерфейсы реализовывать. В верхний слой нужная реализация попадает, соответственно, через какой-либо механизм инверсии зависимостей(DI)

    Небольшое примечание

    Один из слоев - "Data Access Layer", сокращенно DAL, который позволяет получить доступ к базе данных. Главная задача данного слоя - абстрагироваться от источника данных, так как мне нужно поддерживать и mysql и postgresql

    Задача данного слоя - абстрагировать ядро от хранилища, а он уже может использовать любые фичи нужных БД как ему нужно.
    Ответ написан
  • Проектирование структуры приложений для начинающего?

    @EvgeniiR
    https://github.com/EvgeniiR
    Роберт Мартин, "Чистая Архитектура", "Чистый код", "Идеальный программист"
    Макконнелл, "Совершенный код".

    Далее по ситуации, Фаулер, Эванс, Кент Бек и т.п.

    Заменять чтение книг собиранием по крупицам информации в интернете ни в коем случае не советую.
    Ответ написан
    28 комментариев
  • ValueObject - зачем?

    @EvgeniiR
    https://github.com/EvgeniiR
    Value Object`ы всегда валидны. Не получится создать VO "дата", например VO DateTimeImmutable и записать в него 35 апреля 2019 года.
    Они могут состоять из несколких примитивов, например Point из {x,y} или {x,y,z}
    Можно удобненько напихать в него статических конструкторов.
    Можно на уровне типов требовать сразу валидную дату.
    Ответ написан
  • Как избежать дублирования кода в микросервисной архитектуре?

    @EvgeniiR
    https://github.com/EvgeniiR
    Как избежать дублирования кода в микросервисной архитектуре?

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

    Простой пример 2 сервиса используют одну базу и одну таблицу

    Границы проведены явно не правильно.

    модель орм на 1 экран кода, и как мне поступить без копипасты чтобы она была и так и там

    Это даже не SOA, это монолит общающийся по АПИ, выходит

    Закономерный вопрос - для чего вам разделение на микросервисы?
    Ответ написан
    3 комментария
  • Пихать много логики в модель - это Laravel way?

    @EvgeniiR
    https://github.com/EvgeniiR
    Фреймворк -это инструмент для решения ваших задач. Не вы должны подстраиваться под фреймворк, нужно подбирать инструмент подходящий для ваших целей.
    По хорошему вся ваша бизнес-логика вообще никак не должна зависеть от фреймворка, и в этом плане у Yii и Laravel всё не очень хорошо, но Ларавель хоть компонентный, и кастомизировать можно, если понимать что делаешь.
    Ответ написан
    2 комментария
  • Как называть свои свойства и методы при наследовании от других классов?

    @EvgeniiR
    https://github.com/EvgeniiR
    Не проверять же перед добавлением нового свойства/метода нет ли с таким же именем в родительских классах?

    Почему не проверять? + IDE такие вещи автоматически подсказывать должна.

    Ну и по хорошему если уж наследуетесь от чего-то, необходимо понимать для чего вы это делаете, и всё же иметь представлении о родительской структуре
    Ответ написан
    Комментировать