Beego — это уже не Go

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

    Хайп Go пришелся где-то на 2014ый год, когда авторы приложений имевших от силы 1000RPM (requests per minute) вдруг как один решили, что им срочно нужен concurrency, потому что вот-вот их 1000RPM превратиться в 1000RPS (что тоже не так много, на самом деле).

    Результатом хайпа стало то, что к Go приобщилось много людей, привыкших к MVC архитектуре приложения, буть то Spring, Django или Ruby on Rails. И эту архитектуру, как сову на глобус, они стали натягивать на Go. Так появились кадавры вроде Beego и Revel. Revel благополучно сдох, хотя его и пытаются все еще откачать. А вот о Beego хочется поговорить отдельно.

    Немалый вклад в продвижение Beego среди масс вложил Richard Eng своим циклом статей «A word the Beegoist». Практически «Евангелие от Ричарда». Иронично, что не смотря на то, что Ричард оголтело продвигает Go, сам он на нем не пишет.

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

    Давайте разберем несколько основных аспектов Beego, и почему они противоречат различным best practices в Go, да и в индустрии в целом.

    Структура папок


    Robert C. Martin, более известный как Uncle Bob, неоднократно озвучивал идею, что структура приложения должна передавать его суть. Он крайне любит приводить пример с кафедральным собором, на который можно посмотреть сверху, и сразу понять, что это кафедральный собор.

    Роберт неоднократно критиковал Ruby on Rails за его структуру папок — controllers, models, views, вот этого всего. Проблема такого подхода заключается в том, что приложение по продаже носков «сверху» будет выглядеть точно так же, как приложение для заказа еды. И для того, чтобы понять суть приложения, нужно будет забраться в какую-нибудь папку models, и посмотреть, а с какими же сущностями мы в итоге имеем дело.

    Именно это больное поведение Beego и копирует. В то время как тот же Spring ушел в сторону Domain Driven Design и структуры папок передающей суть, Beego навязывает использование структуры, ставшей уже antipattern'ом.

    Но проблема даже серьезней. Для Go нет разделения между структурой папок и структурой пакетов (package'ей). Потому в Beego и UsersController и OrdersController будут под одним package'ем — controllers. А если у вас controller'ы двух типов, те, что сервят UI и те, что используются для API, причем последние в приличном обществе принято версионировать? Тогда будьте готовы к уродцам вроде apiv1.

    ORM


    Довольно странно, что Beego, будучи неудачным клоном Ruby on Rails, при этом не использует ActiveRecord pattern. Его ORM представляет собой крайне странное зрелище. Если для совсем базовых операций, вроде прочесть строку/записать строку, он еще годится, то вот, к примеру, как выглядит простенькая выборка (здесь и далее примеры взяты напрямую из документации):

    qs.Filter("profile__age__gte", 18) // WHERE profile.age >= 18

    Но основная проблема с Beego ORM даже не в том, что нужно бороться с proprietary языком, а в том, что он использует все худшие практики Go, будь то import sideffect'ов:

    import (
        _ "github.com/go-sql-driver/mysql"
        _ "github.com/lib/pq"
        _ "github.com/mattn/go-sqlite3"
    )

    Или регистрация моделей в init():

    func init(){
        orm.RegisterModel(new(User))
    }

    Сделайте себе одолжение, даже если вы все же решите по какой-то необъяснимой причине работать с Beego, не используйте Beego ORM. Если вам без ORM жизнь не мила (а что вы делаете в мире Go, милейший?), пользуйтесь GORM. Он хотя бы поддерживается. Иначе, «database/sql» вам в помощь.

    Bee tool


    Из Ruby on Rails скопирован так же command line tool, который зовется просто Bee. Вот только если в мире RoR был rails и был rake, то bee — это такая мусорка для всего. Он и MVC приложение за'boostrap'ит, и миграции прогонит, и file watcher запустит. В последнем и кроется еще одна проблема. Ведь в чем одно из основных достоинств Go? То, что запускается локально, максимально близко к тому, что запустится в production'е. Если вы не используете bee, конечно.

    Automatic routing


    Go — строго типизированный язык, который при этом не поддерживает ни generics, ни annotations. Как на таком слепить MVC фреймворк? Путем чтения комментов и генерации файлов, конечно.

    Выглядит это примерно так:

    
    // @Param   body        body    models.Object   true        "The object content"
    // @Success 200 {string} models.Object.Id
    // @Failure 403 body is empty
    // @router / [post]
    func (this *ObjectController) Post() {
        var ob models.Object
        json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
        objectid := models.AddOne(ob)
        this.Data["json"] = map[string]string{"ObjectId": objectid}
        this.ServeJson()
    }

    Очевидность, как можно видеть, — нулевая. Функция Post() вообще ничего не получает и не возвращает. http.Request? Нет, не слышали.

    Ну, а как работает весь routing? При запуске пресловутого bee генерируется еще один файл, commentsRouter_controllers.go, который содержит пример такого замечательного кода:

    func init() {
        beego.GlobalControllerRouter["github.com/../../controllers:ObjectController"] = append(beego.GlobalControllerRouter["github.com/../../controllers:ObjectController"],
            beego.ControllerComments{
                Method: "Post",
                Router: `/`,
                AllowHTTPMethods: []string{"post"},
                MethodParams: param.Make(),
                Filters: nil,
                Params: nil})
    ...
    }

    Смотрите, не забудьте перегенерировать и за'commit'ить этот файл после каждого изменения. До последнего времени ситуация была еще печальней, и во время тестов этот файл генерировался автоматически, так что о проблемах вы узнавали уже в production'е. Кажется в последних версиях это странное поведение было исправлено.

    Component testing


    И так мы подходим к теме тестирования. Go, в отличие от большинства других языков программирования, приходит с тестовым фреймворком «из коробки». В целом, философия Go в том, что тест должен сидеть рядом с тестируемым файлом. Но мы же в мире MVC, плевать на философию Go, верно? Потому будьте добры все свои тесты разместить в папочке /test, как завещал нам DHH.

    И это не такая уж мелочь, потому что, напомню, в Go package == folder. И если тест находящийся в том же package'е может вызвать private method, то тест находящийся в другом package — уже нет.

    Но ладно бы все ограничивалось структурой папок. Код Beego в принципе очень сложно тестировать, поскольку в нем все на свете — это side effect.

    Вот так Beego запрашивает routers:

    import (
    	_ "github.com/../../routers"
    )
    

    Та же история и с middleware'ами, и с controller'ами, которые я уже упоминал раньше.

    Документация


    Это для меня как software architect'а вишенка на торте. Документация в BeeGo хороша настолько, насколько хорош ваш китайский. Нет, от комментов на китайском внутри кода за последние года два уже вроде избавились.

    Теперь на китайском остались только некоторые pull request'ы:

    image

    И в особенности в issues:



    Вместо заключения


    Если у вас есть команда написателей кода на Ruby/PHP/Python, и вы срочно хотите перевести их на Go, худшее, что вы можете для них сделать — это заставить их перейти на MVC фреймворк на Go. MVC в целом так себе архитектурный паттерн, а в Go он вообще не к месту. Либо, если вы уж совсем уверены, что ничто кроме Go вас не спасет, пусть переучиваются и пишут так, как в Go принято — максимально плоско и explicit. Либо, быть может им видней, при помощи какого инструмента решать поставленные им задачи?
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 73

      +6
      Ничего не знаю про BEE, но поддерживаю мысль автора, что паттерны MVC и ORM — это красивые абстракции для своего времени, но они разбились о сложность реального мира, и более неактуальны. Предметную область надо нарезать не вдоль (модели, контроллеры, презентеры и прочие технологически-обусловленные сущности), а поперек — то есть объекты или аспекты реального мира, по сути DDD. Строго мое Имхо.
        +1

        MVC это вообще не паттерн, а обобщенный принцип, реализацию которого каждый видит по-своему. Есть, конечно, его описание еще из 80-х, но оно имеет очень отдаленное отношение к реализации в Rails-подобных фреймворках. Я предпочитаю избегать этого термина вообще и использовать более конкретные (скажем, hexagonal architecture).


        А вот с термином ORM все нормально, просто надо вспомнить, как он определен у того же Фаулера. Active Record (или Data Mapper) — это способы реализации ORM. Вручную написанный (скажем, с SQL запросами и Reflection-ом) маппер для конкретной сущности — это тоже ORM.


        По вопросу нарезки согласен: тут из DDD важно одно из ключевых его понятий "Bounded Context".

          +1
          Ну да, я не спец в терминологии, суть в том, что самое живучее оказывается простое и легковесное, а не тяжелое и унифицированное, как большинство фреймворков, поэтому чистый go — это конечно прорыв, и старые унифицированные подходы тута незачем тащить, иначе получим еще одну Джаву.
          PS
          ОRМ не уважаю категорически, когда нужна производительность и отлаживаемость — пишу SQL только вручную, и не разу за 25 лет не пожалел.
            0
            ОRМ не уважаю категорически, когда нужна производительность и отлаживаемость — пишу SQL только вручную, и не разу за 25 лет не пожалел.

            Нужно просто знать, где остановиться. Пытаться натянуть сову на глобус и делать «как в питоне» — заведомо обреченная на неуспех задача. Однако, если использовать ORM в меру, как просто продвинутый конструктор запросов — можно получить много профитов, фактически не отказываясь от ручного написания запросов. По крайней мере, в go.
              0
              Уж как минимум нужно использовать prepared statements. Без них как-то вообще грустно.
              +1
              ОRМ не уважаю категорически, когда нужна производительность и отлаживаемость — пишу SQL только вручную

              Еще раз повторюсь: вручную написанный SQL и ORM вполне совместимы.


              Вот на условном псевдокоде:


              class MyEntity {
                  constructor(public final id: int, public final title: string);
              }
              
              interface MyEntityMapper {
                   findById(id: int) -> MyEntity;
                   persist(myEntity: MyEntity);
              }
              
              MyEntityMysqlMapper.findById(id: int) -> MyEntity = {
                  row := db.query("select id, title from my_table where id = $1", id).fetchOneOrFail();
                  return MyEntity(row.id, row.title);
              }
              
              MyEntityMysqlMapper.persist(myEntity: MyEntity) = {
                  db.query("insert into t (id, title) values ($1, $2) on duplicate key update set title=$2", myEntity.id, myEntity.title);
              }

              Это тоже ORM.

              0
              Интересно, а есть успешные кейсы следования идеям DDD при написании приложух на go?
              Я чаще всего пока слышу такую точку зрения, что один микросервис на go == Bounded context и получается что код в рамках сервиса уже сам по себе сгруппирован по предметной области.
              Ну и прикрываясь этим в project structure самого проекта творится лютая дичь с папками вроде controllers, dbQuery, services и т.д Чего только стоят официальные рекомендации на этот счет.
              Т.е внутри микросервиса файлы группируются по технологическим признакам, т.к считается что вся кодовая база одного сервиса обслуживает небольшую узкую часть предметной области
            +3
            они разбились о сложность
            реального мира, и более неактуальны.


            Как по мне, очень спорное и слишком категоричное утверждение. Может поясните?

            В конце концов, MVC — это всего навсего способ организации обработки http запроса, и он нисколько не противоречит DDD. А, наоборот, чаще всего с ним сочетается.

            Если MVC — это неправильная архитектура организации приложения, то как тогда в go надо? Чем он так принципиально отличается от всего остального back-end мира?
              +5
              MVC — это всего навсего способ организации обработки http запроса

              MVC к HTTP не имеет отношения.
                +2
                Я так понял, что автор комментария имеет ввиду «MVC в контексте бекенда web-приложения — это всего навсего способ организации обработки http запроса»
                –2
                На мой взгляд в новых ЯП надо возвращать старинный способ объектного моделирования сущностей реального мира — класс инкапсулирует в себе все 3 аспекта — данные, логику и представление (вплоть до куска HTML со стилями и скриптами). Это соответствует тренду к микро-сервисным архитектурам. Это соответствует самой жизни, ведь человек умудряется носить с собой свое вью и контроллер внутри одного черепа, если макияж не считать. Принудительно разрезать реальный объект на три уровня MVC — это создавать себе проблемы на ровном месте, пытаясь вместо классического наследования применять композицию, да еще и такую негибкую.
                  +2
                  От solid при таком подходе придется отказаться в первую очередь. А вообще, представляю эти «божественные объекты», хранящие в себе логику обработки запросов, обращения к бд и вывода. Вот уж гибкая, читаемая и прозрачная структура получится. :)
                  Имхо, логика все-таки должна быть разделена по слоям, и даже компонентный подход этому не противоречит. А MVC в свое время как раз и возник, как способ разгрести ту кашу, в которую очень быстро превращается реализация предлагаемого вами паттерна.
                  Впрочем, всему свое время и место, наверное.
                    0
                    Согласен, всему своя цена. Но тенденция в том же go — весь код одного аспекта должен быть расположен рядом, отсюда и проверка ошибок, и defer. Я стараюсь тоже так писать, чтобы не скакать по файлам и выискивать нужный контроллер для данного вью. Возможно, просто у меня нет опыта работы с фреймворками, всегда задачи попадались компактные.
              –9
              В любом языке будут школьники, которые молятся на устаревшие подходы.
              Например, в России студентов натаскивают на ООП и MVC, поэтому у нас в каждой щели ООП и МВЦ, даже когда от них вообще никакого толку.

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

              И работодателю очень сильно кажется, что как раз такие люди для него особенно эффективны. А они ещё и не шибко дороги.

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

              Я всё это к чему. Если миллион мух делает что-то ужасное, это совершенно не значит, что что-то в этом мире не так. Главное, не работать с этими мухами вместе, чтобы не приходилось иметь дело с тем же, что и они.
                +5

                ООП нисколько не устарело. Устарело то понимание ООП, которому учат студентов (при этом в основном учат те, кто сам толком не понимает ООП).


                Модный ныне DDD — это по сути и есть реализация принципов SOLID. Давно уже модные микросервисы — тоже, по сути, ООП: если каждый микросервис рассматривать как объект, получается 1 в 1 определение Алана Кея.

                  0
                  Тысячу раз да. ООП — это самый лучший паттерн из мне известных, и даже Go его поддерживает :)
                –2
                И это не такая уж мелочь, потому что, напомню, в Go package == folder. И если тест находящийся в том же package'е может вызвать private method, то тест находящийся в другом package — уже нет.

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

                  –3
                  В Go в целом нет такой вещи, как private method, все package private.
                    +1
                    С чего вы взяли что тестирование приватных методов это моветон.
                      +2
                      Наверно исходя из того, что тестировать надо поведение и результат, а не «внутренности». Иначе, тест будет по сути тем же, что и тестируемое… только написано сложнее и корявее :)
                        –1
                        Ну просто не всегда возможно проверить тестоми которые используют только публичные методы все граничные условия. Точнее наверно это можно сделать но сложность этих тестов может быть гораздо выше, чем тестов которые могут вызывать приватные методы. И опять смотря что приватный метод делает.
                          0
                          Эту проблему я решаю так: Если мне надо протестировать что-то «приватное», то выделяю это в отдельную сущность, с публичным API.

                          Это кстати так же помогает например с логированием через декоратор, а не прямо в методе, посреди функциональности вызывать логер. Все становится чистым и приятным.
                          0
                          Ага. Именно поэтому во всех железках, от автомобилей до микропроцессоров есть специальные тестовые подключения, к которым обычный потребитель доступа не имеет. Идионы, наверное, всё это проектируют.

                          </sarcasm>

                          Тестировать нужно на всех уровнях: через API делаются инеграционные тесты, а через внутренние методы — низкоуровневые тесты. И они друг друга нифига не заменяют.
                            +1
                            Ага. Именно поэтому во всех железках, от автомобилей до микропроцессоров есть специальные тестовые подключения, к которым обычный потребитель доступа не имеет. Идионы, наверное, всё это проектируют.

                            Плохой пример. Ибо тут точно так же есть публичный API с подключением по «правам/ролям».

                            Тестировать нужно на всех уровнях: через API делаются инеграционные тесты, а через внутренние методы — низкоуровневые тесты.

                            Интеграционные тесты, это тесты, где тестируется ВЗАИМОДЕЙСТВИЕ между системами.

                            Внутренние методы != приватные.

                            P.S. Интеграционное тестирование конечно не взаимозаменяемое с юнит тестированием. Я такого и не говорил и не утверждал.
                              +1
                              Внутренние методы != приватные

                              Возвращаемся к тому, что в Go в целом нет private method, только package private. Если не использовать Beego, конечно, с которым все будет private.
                                0
                                Плохой пример. Ибо тут точно так же есть публичный API с подключением по «правам/ролям».
                                Какой же он «публичный», если там пломба и вам нужно быть «в системе», чтобы безболезненно её менять?

                                По мне — так полный аналог пакетов в Go…
                        0
                        Извините конечно, но с каких пор импорт сайдэффектов, особенно для баз данных — худшая практика? Вообще-то это в стандартной библиотеке так сделано.
                          +2

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


                          Как пример — библиотека логирования glog регистрирует глобальные флаги командной строки при импорте, и требует парсинга этих флагов. Переименовать флаги нельзя, при конфликте возникает panic. Чтобы решить проблемы с glog в Kubernetes, ее в итоге форкнули в https://github.com/kubernetes/klog/blob/master/README.md


                          Пример описания проблем с glog: https://github.com/kubernetes/kubernetes/issues/61006

                          • UFO just landed and posted this here
                              0
                              Глобальное состояние в целом не зло, особенно в Go, где правила несколько изменились и повсеместное применение глобальных package-level переменных и прочего работает и проблем не создает в большинстве случаев. Все таки это идиоматичный Go, а не какая-то редкая практика. Единичные примеры тут мало что значат и говорят, в первую очередь, о странно написанном glog. Либа, которая лезет во флаги командной строки — вот где проблема, а не в глобальном состоянии.
                              +1
                              То, что так сделано в стандартной библиотеке — не значит, что это хорошая практика.
                                0
                                Безусловно не значит, но, с другой стороны, может быть тогда вообще не стоит писать на языке, где стандартная библиотека не отражает хорошие практики :)?
                                  0
                                  Писать fullstack приложения на Go, как это предлагает делать Beego, явно не стоит.
                                    0

                                    Да много где стандартная библиотека кривая — сначала не все продумали, а потом не меняли из соображений обратной совместимости. В PHP, например. Или в Java местами. Это не значит, что надо ждать языка своей мечты до пенсии :)

                                      0
                                      Я бы даже сказал сильнее: на языке, где в стандартной библиотеке все «прямо и красиво» ничего писать не стоит. Потому что вариантов ровно два:
                                      1. Эту библиотеку постоянно меняют и ломают совместимость (см. D).
                                      2. Язык ещё невероятно молодой и на нём мало чего написано, отчего и непонятно — что именно сделано криво в стандартной библиотеке (см. 100500 новых и новейших языков).
                                        0
                                        Ну Go не такой молодой язык, чтобы ему требовалось из соображений совместимости что-то оставлять :). Не думаю, что авторы языка считают "_" импорт ради побочных эффектов за плохую практику.
                                          0

                                          Обычно импорт "_" делается либо для того, чтобы сработал init() пакета, либо чтобы достать что-то импортированное через рефлексию.


                                          Сомневаюсь, что в общем случае это хорошая идея.

                                            0
                                            Да, я собственно про вариант с init() и говорил :).
                                      0
                                      Так же не означает, что практика плохая, если есть плохие примеры ее использования. Практика вполне хорошая, полезная, удобная. Когда применяется правильно проблем не создает, что и показывает пример стандартной библиотеки.
                                    –1
                                    Достаточно надуманная критика, которая явно исходит из личных предпочтений и своего взгляда на best practics. И ещё есть постоянные болезненные отсылки к RoR.
                                    Я ни разу не пользователь beego, и на go уже достаточно давно ничего не писал, но могу отметить, что большая часть пунктов притянута за уши.

                                    >Структура папок

                                    Сильная субъективщина на несколько абзацов текста. Либо фрэймворк позволяет создать свою структуру приложения (пусть даже подпакетами внутри controllers\models\view и т.д.), либо нет. Маловероятно, что в beego есть жесткий лок на структуру директорий.

                                    >ORM
                                    >Но основная проблема с Beego ORM даже не в том, что нужно бороться с proprietary языком, а в том, что он использует все худшие практики Go, будь то import sideffect'ов

                                    Неправда. Подключение драйверов БД через сайдэффект — это штатный способ подключения в go.
                                    Вот синтаксис запросов — более чем странный, тут можно согласиться.

                                    >Bee tool
                                    >Вот только если в мире RoR был rails и был rake, то bee — это такая мусорка для всего

                                    Неправда. Если я правильно помню, в RoR5 команды «rails command» полностью заменяют rake, а в RoR4 почти всё было через rake. Да и в целом разделение не принципиальное. И непонятно, где у автора проблема с filewatcher возникла.

                                    >Automatic routing
                                    >Ну, а как работает весь routing? При запуске пресловутого bee генерируется еще один файл, commentsRouter_controllers.go

                                    Тут можно согласиться, что аннотации в go выглядят несколько странно. Но вот генерация дополнительно файла (от кодогенератора) — так делают все кодогенераторы в go.

                                    >Component testing
                                    >И если тест находящийся в том же package'е может вызвать private method, то тест находящийся в другом package — уже нет.

                                    Если вы хотите постоянно тестировать приватные методы, то у вас более крупные проблемы, чем война с beego.
                                      0
                                      Если вы хотите постоянно тестировать приватные методы, то у вас более крупные проблемы


                                      Не могли бы вы пояснить свою мысль.

                                      0
                                      Мне вот как то наоборот, BeeGo приятен.
                                      По моему опыту, я использовал его — от middleware до back-end. По бек скажу за MVC — как раз благодаря этому, сначала пренес логику с Java на Beego, ну и потом еще раз логику с обычного контейнера на AWS Lambda
                                      Проблема такого подхода заключается в том, что приложение по продаже носков «сверху» будет выглядеть точно так же, как приложение для заказа еды.

                                      Да в общем то по бизнес логике, это практически одинаковые процессы, отличающиеся нюансами предметной области.
                                      Тогда будьте готовы к уродцам вроде apiv1

                                      И в чем проблема в поддержании версий апи живыми? Очень похоже реализована версионность в AWS
                                      А насчет кодогенерации, то что есть в Beego гораздо помягче Dagger'a
                                        +1
                                        это практически одинаковые процессы, отличающиеся нюансами предметной области

                                        Так предметную область и надо выставлять на показ.

                                        И в чем проблема в поддержании версий апи живыми?

                                        Проблема не в поддержании API, проблема в том, что теряется вся семантика.

                                        А насчет кодогенерации, то что есть в Beego гораздо помягче Dagger'a

                                        С Dagger не работал. Там тоже приходится сгенерированный код коммитить?
                                          +1
                                          С Dagger не работал. Там тоже приходится сгенерированный код коммитить?

                                          Я не работал с Go и Beego, но работал с многими системами где было много кодогенерации. Весь этот процесс успешно встраивался в Build.
                                          То есть генерируемый файл не коммитился в репозиторий, а генерировался в процессе создания Build-а в Bamboo, который впоследствии деплоился на сервер.


                                          Чем вызвана нужда генерировать файл локально?

                                            0
                                            Там тоже приходится сгенерированный код коммитить?

                                            В Go, насколько я помню, принято коммитить сгенерированный код

                                              0
                                              О! Еще одна глупость в гоу. Которая полностью паскудит статистику и делает менее юзабельными дифы.
                                                0
                                                Зачем строить статистику по сгенерированному коду? Зачем смотреть диффы по сгенерированнмоу коду?
                                                  0
                                                  Я тоже не понимаю зачем. Зачем она коммитится и, в результате, строится автоматически?
                                              0
                                              Так предметную область и надо выставлять на показ.

                                              Вот тут можно подробней. На мой взгляд, достоинство систем с солид -подходом (mvc, IMHO, это проекция Single Responsibility Principle с выделением отображений. Просто логичный способ разделения логики приложения) это то что их просто изменять и рефакторить. Вот как тут более на показ выставить предметную область?
                                              проблема в том, что теряется вся семантика

                                              Вот тут можно подробней. У себя я семантику апи реализовывал как json на вход. Ну если не лень, можно и полный GraphQL распарсить.
                                              ну и чтоб два раза не вставать,
                                              эту архитектуру, как сову на глобус, они стали натягивать на Go

                                              mvc, IMHO, это проекция Single Responsibility Principle на вот это все с отображением. Просто логичный способ разделения логики приложения. И как то без разницы, на каком языке.
                                                0
                                                Вот как тут более на показ выставить предметную область?

                                                Вместо:
                                                root/
                                                --controllers/
                                                ----userController
                                                ----orderController
                                                --models/
                                                ----user
                                                ----order
                                                --views/
                                                ----user.view
                                                ----order.view


                                                Можно иметь
                                                root/
                                                --user/
                                                ----user
                                                ----userController
                                                ----user.view
                                                --order/
                                                ----order
                                                ----orderController
                                                ----order.view


                                                У себя я семантику апи реализовывал как json на вход

                                                А как быть с семантикой внутри приложения? В том же JavaScript'е хотя бы namespacing при импорте нормальный:
                                                import { UsersController } from './api/v1/usersController.js'

                                                В Go так не выйдет.
                                                  0
                                                  Можно иметь
                                                  И если в order.view используется user.model, а в user.view — order.model, то у нас получается спагетти. С классическим разделением контроллеров-вьюшек-моделей такая проблема проблема проявляется значительно меньше.
                                                    0
                                                    С чего бы? Количество классов в обоих примерах одинаковое, зависимости те же.
                                                      +1
                                                      А от пересечений между неймспейсами — больше. И куда бы вы отнесли какие-то совмещенные вьюшки? Ну типа таблица orders <=> users.

                                                      Ну вот у вас есть модели Ученик и Урок:
                                                      root/
                                                      --student/
                                                      ----student
                                                      ----studentController
                                                      ----student.view // <== личная страница ученика
                                                      --lesson/
                                                      ----lesson
                                                      ----lessonController
                                                      ----lesson.view // <== на этой странице у нас информация про урок, кто его преподает и в каком классе

                                                      в какую из ваших папочек вы бы закинули табель результатами, где по вертикали — список учеников, а по горизонтали — список уроков?
                                                        0
                                                        И куда бы вы отнесли какие-то совмещенные вьюшки? Ну типа таблица orders <=> users.

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

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

                                                        Табель — вообще отдельный domain, который я бы занес в какую-нибудь /grading
                                                          0
                                                          Зависит от того, где это будет применяться. Скорее всего пользователь хочет видеть свои заказы, а не наоборот.
                                                          По такому принципу в папке orders вообще не должно ничего лежать. Или у вас бывает, когда заказы хотят что-то увидеть?

                                                          Табель — вообще отдельный domain, который я бы занес в какую-нибудь /grading
                                                          Ага, значит список папок — это, по сути, список вьюшек, в которых иногда лежат модели. Проблема в том, что создавая новую вьюшку — как вы можете посмотреть модели, которые могли бы в ней использовать?
                                                            0
                                                            По такому принципу в папке orders вообще не должно ничего лежать. Или у вас бывает, когда заказы хотят что-то увидеть?

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

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

                                                            Domain — это не view.
                                                            Если посмотреть на те проекты, с которыми я сейчас работаю, у меня в domain'е будет скорее всего аналог controller'а, а может и не один, и какой-то набор service class'ов, причем произвольное количество слоев, в зависимости от сложности.
                                                              0
                                                              Просто на практике, а не в сферических примерах обычно модели и вьюшки относятся к разным доменным зонам, то есть не пересекаются напрямую, как вы написали и просто их соотносить вот модель, а вот её вьюшка — крайне неудобно. Получается, пишется модель, их взаимосвязи, возможно даже модель дублируется и на сервере, и на клиенте. Потом отдельно на эти модели направляются вьюшки. Они могут быть разными. Скажем, mobileView и desktopView. А модель — она одна. И часто на эти направления нужны разные люди. Кто-то лучше верстает и шрифты подгоняет, а кто-то лучше предметную область и взаимодействие компонентов понимает.

                                                              В итоге в игрушке есть сущность автомата в модели, которая используется и на карте, и в руках воина и в инвентаре и в каждом месте по разному. А модель — одна. И получается нам тогда надо WeaponDomain, в нее набросать все вьюшки, которые будут использовать уже другие компоненты. Что часто ужасно неудобно, т.к. крайне размазывает ответственность. У тебя всё зависит от всего, куча перекресных ссылок.

                                                              Намного удобнее, когда тебе дали задание изменить размещение элементов в инвентаре — ты зашел в view/inventory, а там — все элементы. А потом тебе дали задание иначе баллистику пули и способ рассчета повреждений реализовать — пошёл в модель, поменял там это.

                                                              Просто лично я сам когда писал лет 8 назад свой фреймворк — старался делить именно на сущности, а внутри уже на модель и отображение, но как-то со временем понялось, что это плохой подход и модель значительно удобнее на практике как отдельная сущность.

                                                              Хотя я предполагаю, что на каких-то примитивных задачах этот подход будет работать, но там любой подход будет работать.
                                                                0
                                                                Мы как-то перескочили на написание игрушек и собственных фреймворков.
                                                                  +1
                                                                  Да дело в том, что разделение на модель/вьюшку — универсально, и хорошо расширяется. А разделение по доменам — как повезет. В каких-нибудь средне-крупных сайтах будут те же проблемы, просто пример игрушки — очевиднее.

                                                                  А вообще мы как раз изначально говорили о фреймворке, разве нет? (с него мы начали, а не к нему пришли) — авторы целятся на более универсальное решение, которое не превратится в лапшу, стоит сделать шаг в сторону.
                                                                    0
                                                                    Возвращаемся к тому, что разделение по доменам не отменяет разбиение на модели и view.

                                                                    Если под «крупным сайтом» подразумевается какое-нибудь монолитное приложение, а не набор микросервисов, делим его на модули. Каждый модуль — domain. Ко всему есть подход. Но если DDD не нравится — заставить его полюбить не смогу, да и не нужно :)

                                                                    А по поводу конкретного фрейморка — авторы никуда особо не целились, а тупо скопировали то, что было в Ruby on Rails.
                                                                      0
                                                                      Но если DDD не нравится — заставить его полюбить не смогу, да и не нужно
                                                                      То есть аргументов нету и надо поверить и полюбить наивной любовью?
                                                                        0
                                                                        То есть у меня в целом нет цели убеждать любить DDD кого-то, с кем я не работаю :)
                                            0
                                            как в Go принято — максимально плоско и explicit

                                            Это верно подмечено, иначе просто не получится. Да и вообще понравились мысли в заключении. Я вот перехожу с java like языков на golang ну идеологически ахереваю переход дается очень тяжело.
                                            При написании кода складывается ощущение что вернулся лет на 5 назад — ни нормального ddd, grasp, rich domain models, hexagonal architecture — ничего из этого не требуется, и более того, порицается со словами «это не go way»
                                            Вообще я вижу go как язык с довольно узкой нишей(в которой он чертовски хорош). И очень грустно от того, что все почему то увидели в нем серебряную пулю, способную заменить enterprise языки. Из за попыток таких замен и получаются такие недоразумения как фреймворк, которому посвящена статья.
                                              0
                                              Go очень легко продавать менеджменту — «низкий порог входа, нанимаем джунов, concurrency, 100K RPS, все дела».
                                                0
                                                а в какой нише он чертовски хорош? Просто не в теме, интересно
                                                  0
                                                  У Go есть одно хорошее применение — инфраструктура. Какая нибудь command line utility или reverse proxy на нем действительно пишутся хорошо. Все остальное — так себе.
                                                    0
                                                    Ха! Так они на любом языке хорошо пишутся, а весь этот оверхэд, который так поносится в статье и, я так понимаю, Go-разработчиками, тоже не на пустом месте возник — как только нужно что-то монструозно большое, я думаю что на Go, что на Java, что на .NET будут и фасады, и микросервисы, и вся эта братия
                                                      0
                                                      как только нужно что-то монструозно большое, я думаю что на Go, что на Java, что на .NET будут и фасады, и микросервисы, и вся эта братия

                                                      Безусловно. Нет в Go никакой магии, хоть некоторые в нее и веруют.

                                                      Так они на любом языке хорошо пишутся

                                                      Не совсем. В Java до последнего времени не было удобного HTTP Client «из коробки», к примеру. Какой-нибудь command line на Python'е потребует предустановленный интерпретатор, в то время как в Go получаем единый бинарник «из коробки», опять же.
                                                0
                                                Использую Beego постоянно, про DDD и ORM согласен, ужас ужасный.
                                                Но не всё так плохо, изучал другие фреймворки, во многих разработка вообще встала 3-4 года назад.
                                                В Beego мне нравится роутинг, шаблоны, получение параметров запроса, файлов запроса и тп, контроллеры содержат кучу полезных мелочей для облегчения разработки, получается не думать про организацию и работу кода, а сосредоточится только на реализации, при том вес фреймворка маленький.
                                                Для примера переписал свой средний сайт с аудиофайлами, личный кабинет полностью, управление файлами, панель администратора, весь фронт — за 2 недели (вечерами от основной работы) и сайт весит теперь 10Мб RAM против ~500Мб на php (+ memcache столько же), ну а скорость работы просто самолёт )) Поэтому можно закрыть глаза на временное отсутствие нормальных DDD фреймворков.
                                                  0
                                                  можно закрыть глаза на временное отсутствие нормальных DDD фреймворков


                                                  Не думаю, что временное.
                                                  0

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


                                                  Главная проблема в современном программировании, что большинство читает всяких Фаулеров и Мартинов, но почти никто не читает Таненбаума, Кернигана и других умных людей, которые реально учёные и инженеры, а не "консультанты".


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

                                                    0
                                                    Боюсь предположить, как знания внутреннего устройства процессора помогут написателю кода на какой-нибудь Java, которому нужно туда-сюда гонять JSON'ы по сети.

                                                  Only users with full accounts can post comments. Log in, please.