Предоставляет ли 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 отдельных подвопроса:
- Можно ли использовать статические методы для объявления маршрутов BedSheet?
- Можно ли создавать грани маршрутов для простыни?
Общей темой этих двух вопросов является постоянная ясность и поддержка. Это может быть довольно личная "вещь", и, как у пресловутого кота, есть более чем один способ скинуть ее.
Во всяком случае, решать их индивидуально:
Q). Можно ли использовать статические методы для объявления маршрутов BedSheet?
А). Да, это хорошо (но читайте дальше...)
Q). Можно ли создавать грани маршрутов для простыни?
А). Короче да. В длинных...
Как следует из этого (перефразированного) вопроса - ни BedSheet, ни IoC не имеют никаких аспектов для объявления служб или методов обработчика маршрутов. Во многом это связано с тем, что не считается, что такие аспекты и связанные с ними услуги являются "основными" для включения в структуры.
Сохранение конфигурации маршрутов и служб в AppModule
означает, что его легко найти и отслеживать - особенно для новичков в кодовой базе.
В более крупных проектах децентрализованная конфигурация через фасеты может вызвать незначительные проблемы с обслуживанием. Ведь если вы используете фасеты, единственный способ узнать, какие у вас сервисы, - это текстовый поиск. То же самое относится и к маршрутам. Новому человеку, пытающемуся разобраться в проекте, придется пробираться по различным страницам результатов поиска. В то время как простой взгляд на AppModule
возвестит то же понимание.
Слово can специально выбрано, потому что при достаточном усердии в кодировании, будь то наименование классов или структура каталогов, сервисы и маршруты становятся логически сгруппированными и их легко найти.
В Tales Framework есть грани для объявления маршрутов, но об этом можно сказать в разделе "Внешние маршруты":
Определение маршрутов вместе с методами в качестве аспектов это круто и быстро, но имеет следующие недостатки:
- Не ясно определяет порядок, в котором будут выбраны маршруты
- Вы не можете видеть все маршруты, которые определяет ваше приложение, в одном месте.
Таким образом, объявление сервисов и маршрутов с аспектами не плохо, просто будьте осторожны с этим. Тем не менее, я думал о реализации 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.