Closed nicerobot closed 6 years ago
It is not intended for main.go
to be re-generated. There are two types of files that goagen
generates:
goagen
will not override the scaffold files if they already exist. The idea is that these files belong to you, you should maintain them, test them, change them etc. The initial generation is just to help get things started. On the other hand the generated files should not (and cannot) be modified, the tool will clobber the entire generated package contents during each generation.
The bootstrap
command - as its name indicates - is supposed to be used when starting a new project, once the project is setup the app
, client
, swagger
etc. commands can be used instead. There are a couple of action items that would help here:
go:generate
comment to call the individual generators instead of bootstrap
as per the above.I'll leave this issue opened until these are done.
Thanks. I understand all that and I do agree that it can be clarified in the docs.
This issue isn't about a misunderstanding. It's about a change to allow for more opportunities for code generation. If i add a new resource to the design, i have to update the controller mountings that are coded into the main. To avoid doing that manually, a separation of that information from main.go
will make it easier to regenerate that information. e.g. If i know that i'll never touch service.go
manually, i can update the go:generate to always overwrite service.go
. And main.go
will never be touched with respect to goa because it'll almost always contain customizations.
Let me put it another way. main()
is central to ever app. It shouldn't contain anything generated by goa so that it's easier to regenerate that information if the user wants to. But since main()
will always be customized, it means the goa-generated information in main()
will always have to be maintained manually.
OK thank you for the clarification and sorry for assuming too much :) I have to admit I'm a little bit conflicted on this. On one hand what you are describing would indeed make it easier to re-generate the main code on the other hand it would be promoting a process that in the end might confuse things even more. Today the explanation is roughly: "everything in the main package can be generated once to help you get started then it's all yours", with this change it becomes "everything in the main package is all yours except service.go
which you can re-generate by doing goagen main|bootstrap
which has a special rule about this file.
Another issue is that the controller constructor functions are often modified to pass references to additional components (database handlers etc.). The code generated by goagen
that instantiates and mounts them would thus have to be modified further, defeating the intent.
Finally as you point out the only changes required to main.go
have to do with adding or removing calls to the MountXXX
functions when the set of design resources change. These changes are simple and don't occur often so at this point leaving the main
code generation the way it is seems like the better option.
I agree with the concern of confusion. But for a code-generation tool, I lean towards never implementing anything manually that can be generated from the declarative design. In other words, the design must always be the source of truth but it might not be reflected accurately in the main package if allowed to be managed manually. The manual updates can eventually diverge from the declarative design and that should be frowned upon. Maybe my solution isn't ideal but I do think something should be done. Possibly a template solution where the declarative sections are tagged template blocks.
Understood, one additional comment though: there does not have to be a one to one between the resources described in the design and the controllers mounted on a given service. That is why the MountXXX
functions exist: to give you the flexibility to mount - or not - a given controller on a given service. There is an interesting pattern which consists of writing a single design that is backed by multiple services. This allows for a single / consistent set of docs and client package / tool but makes it possible to use e.g. CQRS or otherwise split the API implementation into multiple microservices in a way that does not affect clients. So while I agree that the concern of implementation diverging from design is an important one I don't think it applies in this specific case.
Let's look at it differently. Regardless of how the services are implemented, I still want to be able to generate the code from goagen
and not type the updates manually. If there's a means of generating the code based on the design, even if it's output to stdout, that's better than giving up on the service-generation code after the initial generation.
The problem is that this code cannot be automatically generated. There's an example that the tool produces to help but it's just one way to hook things up which is helpful if you're starting with goa. It may not be the best way for you and certainly isn't the only way. You need to write this code - goa cannot do it for you - for example it doesn't and shouldn't know about all the dependent modules that need to be setup in main and passed down to the controllers.
goa generates the MountXXX
functions and the service code must call them providing the implementations of the controller interfaces - that's the contract - and these functions is what has to be (and is) kept up-to-date with the design automatically.
Maybe goagen
could generate a TBD.txt
file that lists the MountXXX
functions and the other responsibilities of the user code such as setting up the security middlewares.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
main()
should never contain anything generated by the design. By generating content inmain()
it means after I customize mymain()
(always, e.g. to add TLS and CLI support), if my design changes i have to copy and paste updates or manually edit changes into my regeneratedmain()
.What should be generated is something like a
service.go
that contains afunc service() *goa.Service {...}
which takes over for the body ofmain()
.You can see my example/problem here.