I'm playing with Fantom's afBedSheet framework and in its documentation here, the example goes...
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))
}
}
...
The contributeRoutes method above starts becoming hard to read and maintain when more and more Routes are added, especially when route handlers come from different classes.
I'm doing this differently: on each Service class I'm adding a static method that returns a list of Routes handled by its methods, like in this example:
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)
}
}
Then my AppModule looks like this
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) }
...
}
I'm trying to keep the AppModule clean and the Route definition and handler mapping closer to the implementing class. I expect this to make services/routes easier to maintain, but I'm not sure whether it is a good or bad idea. Benefits that I find doing this are
- If I add a route handler method to a class, I declare the Route on the same class
- Since route handler methods are part of the same class, I only have to type the slot name (e.g. #podVersion instead of Info#podVersion) which to me seems easier to read.
But as I said, I'm only playing with afBedSheet and I'd like to know from someone who has done a real production project with this framework if there is a good reason to declare the routes in the AppModule class, as the example shows.
Also, if what I'm doing is Ok or good, I'm wondering if there are (or if it would be a good idea to add) facets to change my Info class above to something more like:
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)
}
}
If facets like these don't exist, I guess there must be good reasons to keep routes declaration in the AppModule and I'd like to know what they are.