Предоставляет ли afBedSheet фасеты для обозначения классов как служб и методов как обработчиков маршрутов?

Я играю с фреймворком afBedSheet от Fantom, и в его документации приведен пример...

using afIoc
using afBedSheet

class HelloPage {
  Text hello(Str name, Int iq := 666) {
    return Text.fromPlain("Hello! I'm $name and I have an IQ of $iq!")
  }
}

class AppModule {
  @Contribute { serviceType=Routes# }
  static Void contributeRoutes(OrderedConfig conf) {
    conf.add(Route(`/index`, Text.fromPlain("Welcome to BedSheet!")))
    conf.add(Route(`/hello/**`, HelloPage#hello))
  }
}
...

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

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

using afBedSheet

class Info {

    static Route[] routes() {[
        Route(`/info`, #all),
        Route(`/info/pod`, #podAll),
        Route(`/info/pod/name`, #podName),
        Route(`/info/pod/version`, #podVersion),
    ]}

    Text all() {
        Text.fromJson(["This application blah blah blah"])
    }

    Text podAll() {
        pod := Pod.of(this)
        return Text.fromPlain("$pod.name $pod.version.toStr")
    }

    Text podName() {
        Text.fromPlain(Pod.of(this).name)
    }

    Text podVersion() {
        Text.fromPlain(Pod.of(this).version.toStr)
    }

}

Тогда мой AppModule выглядит так

using afIoc
using afBedSheet

class AppModule {

    @Contribute { serviceType=Routes# }
    static Void contributeRoutes(OrderedConfig conf) {
        Info.routes.each { conf.add(it) }
        AnotherService.routes.each { conf.add(it) }
        YetAnotherService.routes.each { conf.add(it) }
        ...
}

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

  • Если я добавляю метод обработчика маршрута в класс, я объявляю маршрут в том же классе
  • Так как методы обработчика маршрута являются частью одного и того же класса, мне нужно только ввести имя слота (например, #podVersion вместо Info # podVersion), которое мне кажется более простым для чтения.

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

Кроме того, если то, что я делаю, хорошо или хорошо, мне интересно, есть ли (или было бы неплохо добавить) фасеты, чтобы изменить мой класс Info выше на что-то вроде:

using afBedSheet

@Service // Assuming there is such facet to let afBedSheet discover services
class Info {

    @Route { `/info` }  // <- Route handler facet
    Text all() {
        Text.fromJson(["This application blah blah blah"])
    }

    @Route { `/info/pod` }
    Text podAll() {
        pod := Pod.of(this)
        return Text.fromPlain("$pod.name $pod.version.toStr")
    }

    @Route { `/info/pod/name` }
    Text podName() {
        Text.fromPlain(Pod.of(this).name)
    }

    @Route { `/info/pod/version` }
    Text podVersion() {
        Text.fromPlain(Pod.of(this).version.toStr)
    }

}

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

1 ответ

Решение

В этом (хорошо сформулированном вопросе) я читаю 2 отдельных подвопроса:

  1. Можно ли использовать статические методы для объявления маршрутов BedSheet?
  2. Можно ли создавать грани маршрутов для простыни?

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

Во всяком случае, решать их индивидуально:


Q). Можно ли использовать статические методы для объявления маршрутов BedSheet?

А). Да, это хорошо (но читайте дальше...)


Q). Можно ли создавать грани маршрутов для простыни?

А). Короче да. В длинных...

Как следует из этого (перефразированного) вопроса - ни BedSheet, ни IoC не имеют никаких аспектов для объявления служб или методов обработчика маршрутов. Во многом это связано с тем, что не считается, что такие аспекты и связанные с ними услуги являются "основными" для включения в структуры.

Сохранение конфигурации маршрутов и служб в AppModule означает, что его легко найти и отслеживать - особенно для новичков в кодовой базе.

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

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

В Tales Framework есть грани для объявления маршрутов, но об этом можно сказать в разделе "Внешние маршруты":

Определение маршрутов вместе с методами в качестве аспектов это круто и быстро, но имеет следующие недостатки:

  1. Не ясно определяет порядок, в котором будут выбраны маршруты
  2. Вы не можете видеть все маршруты, которые определяет ваше приложение, в одном месте.

Таким образом, объявление сервисов и маршрутов с аспектами не плохо, просто будьте осторожны с этим. Тем не менее, я думал о реализации REST API на основе аспектов, аналогичных JAX-RS (Java API для RESTful Services)!

Кроме того, конфигурация фасетов тривиальна для реализации; например, если у вас был фасет под названием @Service что вы поместили в классы обслуживания IoC (или mixins), тогда вы можете просто добавить следующую строку в ваш метод связывания:

const class AppModule {
    static Void bind(ServiceBinder binder) {
        AppModule#.pod.types .findAll { it.hasFacet(Service#) }.each { binder.bind(it) }
    }
}

Подводить итоги:

Если вы несете единоличную ответственность за базу кода или работаете над небольшим проектом, тогда использование фасетов - это нормально. Если обслуживание принадлежит другим, или если проект нетривиален, я бы посоветовал сохранить конфигурацию в одном AppModuleили в нескольких модулях, как определено фасетом @SubModule.

Другие вопросы по тегам