You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Earlier I illustrated the architecture of Revel Framework along with my
misunderstanding of what "convention" means. To summarize what was said:
Revel Framework consists of two parts:
revel/cmd package implements a command line tool for:
bootstrapping your applications;
recompilation and hot reloading them;
generation of main.go file;
generation of reverse routes;
starting integration tests;
building applications;
packaging them.
revel package is responsible for everything else:
parsing of configuration files;
parsing of routes (on every start of the app);
execution of templates;
allocation and start of http.Server;
allocation and initialization of all kinds of parameters for:
Session
Cache
Flash
Validation
value binding;
Drawbacks
User app is tightly coupled with the revel package and if they don't like something
about its implementation aspects they have to ask about making changes to the core code base.
With this approach it is not possible to:
Stop embedding revel.Controller;
Do not use revel.Result;
Use custom configuration format of choice;
Use alternative implementation of router;
Replace template package;
Slow releases: large code base that cannot be broken without releasing the next version, the next version cannot be released till enough changes are made;
A lot of unnecessary for a user dependencies;
The framework is difficult to maintain: monolithic codebase, expected behaviour of its components is not defined:
when user embeds revel.Controller instead of *revel.Controller, it works but shows Error 500, we have to guess whether it is by design or a bug.
Mechanisms of Extendability
Startup hooks - implemented as a global revel.OnAppStart variable of type []func() (now a bit more complex than that to support ordering);
Interceptors - similar to Startup Hooks but for special Actions that can be started Before, ..., After regular Actions.
Filters - similar to Startup Hooks but work on a level of HTTP Handler function.
Drawbacks
Too many ways to define middleware: interceptor functions vs. interceptor methods vs. filters;
No way for a middleware to share something with controllers, for Session, Cache, Flash, Validation this is solved by manually adding respective fields
to the revel.Controller{} struct.
I.e. for some new middleware CSRF that means that in order to share CSRFToken with actions of controllers
there are a few options:
Add one more field to the revel.Controller{} struct;
Pass the value using some field that already exists:
c.Controller.Session, c.Controller.Flash (not always possible, e.g. I may want to pass int type or some struct; and requires an allocation of a map even if I don't need a session but just a single variable of scalar type).
Default Layout
app/
controllers/ - imports revel package;
routes/ - imports revel package. Automatically generated by revel/cmd and cannot be commited due to .gitignore;
tmp/ - entry point of the application,
imports: revel, revel/testing, revel/modules/*, controllers, tests.
Automatically generated by revel/cmd and cannot be commited due to .gitignore;
views/
conf/
app.conf - INI configuration file;
routes - a list of routes in a custom format inspired by Play Framework;
messages/
public/
tests/ - integration tests that can be run by revel/cmd.
Drawbacks
app/ directory is a rudiment of Play Framework 1 that was written in Java where it is a regular
practice to have a lot of nested directories. Go projects may have a more flat structure;
app/tmp/ and app/routes/ are not part of user app (they are in .gitignore), thus builds are not reproducable and revel/cmd is required to build/start an app;
conf/routes is not type safe, validated at start time, changes at the stage when the project has already been compiled
lead to unexpected result (Solutions TBD);
Implement everything that Revel provides as a set of independent tools.
Every single one should be usable on its own:
harness/ - hot reloads user applications;
main.go - entry point of the tool so it can be used independently;
runner/ - package that implements some Handler interface;
bootstrap/ - generates a new application from a specified skeleton;
main.go - entry point of the tool so it can be used independently;
creator/ - package that implements some Handler interface;
handler/ - generates standard handler functions from Revel-like controllers;
...
routes/ - generates type safe reverse routes;
...
TBD
Turn revel package into a simple command that imports the tool packages and starts their Handler functions.
Generated Code
Generated code should not depend on revel or any other projects if it's not possible to replace
the dependency easily. The standard library should be relied on as much as possible.
New Definition of Action
Any method that returns a type implementing standard http.Handler interface
as a first argument is an Action.
// App is a controller.typeAppstruct {
}
func (c*App) Index(...) http.Handler {
returnc.Render()
}
// Smth isn't.typeSmthstruct {
}
Extendability
Special Actions
Before is a special action that will be executed before every regular action.
After is an equivalent of Before but with a finally semantics meaning that it will be guaranteed
to be started after every regular action.
Binding
Actions can bind not only built-in types (int, uint, string, float32, etc.) but also http.Request,
types implementing http.ResponseWriter interface, and any other types that have a special Bind(url.Values) method (TBD).
That would allow us to use the Special Actions as Filters (in Revel's terminology):
Use of anonymous embedding requires every Controller to have a unique name. Solutions TBD.
Controller auto allocation rules TBD.
Startup Hooks
Functions for running some controller's code after init() but before the app is started
are still needed. One of the options is to have a special Init function reserved for that:
funcInit() {
}
Or alternatively, auto start any function that is:
This is a discussion of #2 and #4.
Current Architecture
Earlier I illustrated the architecture of Revel Framework along with my
misunderstanding of what "convention" means. To summarize what was said:
revel/cmd
package implements a command line tool for:main.go
file;revel
package is responsible for everything else:Drawbacks
revel
package and if they don't like somethingabout its implementation aspects they have to ask about making changes to the core code base.
revel.Controller
;revel.Result
;revel.Controller
instead of*revel.Controller
, it works but shows Error 500, we have to guess whether it is by design or a bug.Mechanisms of Extendability
revel.OnAppStart
variable of type[]func()
(now a bit more complex than that to support ordering);Before
, ...,After
regular Actions.Drawbacks
Session
,Cache
,Flash
,Validation
this issolved by manually adding respective fields
to the
revel.Controller{}
struct.CSRF
that means that in order to shareCSRFToken
with actions of controllersthere are a few options:
revel.Controller{}
struct;c.Controller.Session
,c.Controller.Flash
(not always possible, e.g. I may want to passint
type or somestruct
; and requires an allocation of amap
even if I don't need a session but just a single variable of scalar type).Default Layout
app/
controllers/
- importsrevel
package;routes/
- importsrevel
package. Automatically generated byrevel/cmd
and cannot becommit
ed due to.gitignore
;tmp/
- entry point of the application,imports:
revel
,revel/testing
,revel/modules/*
,controllers
,tests
.Automatically generated by
revel/cmd
and cannot becommit
ed due to.gitignore
;views/
conf/
app.conf
- INI configuration file;routes
- a list of routes in a custom format inspired by Play Framework;messages/
public/
tests/
- integration tests that can be run byrevel/cmd
.Drawbacks
app/
directory is a rudiment of Play Framework 1 that was written in Java where it is a regularpractice to have a lot of nested directories. Go projects may have a more flat structure;
app/tmp/
andapp/routes/
are not part of user app (they are in.gitignore
), thus builds are not reproducable andrevel/cmd
is required to build/start an app;conf/routes
is not type safe, validated at start time, changes at the stage when the project has already been compiledlead to unexpected result (Solutions TBD);
tests/
cannot be run withoutrevel/cmd
.Sample Code
app/controllers/init.go
app/controllers/app.go
Proposal
Tools
Implement everything that Revel provides as a set of independent tools.
Every single one should be usable on its own:
harness/
- hot reloads user applications;main.go
- entry point of the tool so it can be used independently;runner/
- package that implements someHandler
interface;bootstrap/
- generates a new application from a specified skeleton;main.go
- entry point of the tool so it can be used independently;creator/
- package that implements someHandler
interface;handler/
- generates standard handler functions from Revel-like controllers;routes/
- generates type safe reverse routes;Turn
revel
package into a simple command that imports the tool packages and starts theirHandler
functions.Generated Code
Generated code should not depend on
revel
or any other projects if it's not possible to replacethe dependency easily. The standard library should be relied on as much as possible.
New Definition of Action
Any method that returns a type implementing standard
http.Handler
interfaceas a first argument is an Action.
New Definition of Controller
Any struct type that has actions is a controller.
Extendability
Special Actions
Before
is a special action that will be executed before every regular action.After
is an equivalent ofBefore
but with a finally semantics meaning that it will be guaranteedto be started after every regular action.
Binding
Actions can bind not only built-in types (
int
,uint
,string
,float32
, etc.) but alsohttp.Request
,types implementing
http.ResponseWriter
interface, and any other types that have a specialBind(url.Values)
method (TBD).That would allow us to use the Special Actions as
Filters
(in Revel's terminology):Embedding of Controllers
controllers/init.go
controllers/app.go
Drawbacks
Startup Hooks
Functions for running some controller's code after
init()
but before the app is startedare still needed. One of the options is to have a special
Init
function reserved for that:Or alternatively, auto start any function that is:
error
.Details TBD.
New Layout
assets/
- autogenerated assets;handlers/
- handler functions generated from controllers;routes/
- reverse routes;config/
controllers/
- controllers are splitted into independent packages (TBD);account/
profile/
smthelse/
routes/
- importsassets/handlers
(TBD);static/
views/
main.go
- entry point of the application, importsnet/http
,routes
, and starts web-server;revel.ini
- configuration of the project: how to build it, run, and package. If not presented,default settings (by convention) will be used.
Motivation
go run
is everything that is needed for start of the project;revel
package, only on the standard library;Other ideas
More value for the end users
development in Go (i.e. the same language for both client and server side).
The text was updated successfully, but these errors were encountered: