Code generation for backend and frontend.
When, why and how?Pavel BernshtamBlockedUnblockFollowFollowingMay 22I want to show in this article how we generate backend (and a little frontend) code in our company, why we need code generation and what are some of the code generation techniques we use.
What exactly we generate — it is not important at all.
How we do this — may be interesting for people, who use Gradle in their projects and what to do something similar.
The motivation of code generation and what are sources of generation — those are interesting subjects to discuss.
We describe here 3 kinds of objects, which are the sources for all code generation projects of frontend-backend interaction and, sometimes, full backend implementation.
Messages — objects which participate in frontend/backend interaction in a serialized to JSON form.
Endpoints — URI which is called by frontend, with description of its HTTP method, query parameters, type of a body and responserequest3.
Entities — messages which are persistent on backend side and require DAO layer generation, along with standard endpoints for Create/Update/List/DeleteI’m no expert on frontend, but some things I do know:1.
In our company, frontend is written in Typescript, so I want to generate Typescript classes for messages as well2.
Many of the requirements to the backend (at least on the Controllers layer) come from frontend people.
Backend code requirementsSo, here are the backend requirements from the frontend side:REST-like backend APIUniform responses — JSON, payload in “data” field3.
Uniform error responses — JSON, message in “error” field, in development environment stack trace in “stacktrace” field4.
“Correct” HTTP codes — 404 if an entity not found, 400 if json is not valid etc.
Backend requirements from my side:1.
Error handling in one place2.
Possibility of breaking a flow of business logic in any place and saying what HTTP code and message I want to send back to frontend3.
Some business logic I would like to write in a blocking way (for example if I use a blocking library), some — in async way.
But both should work well in my async framework.
Backend developers should not think about requests and responses, HTTP codes, Controllers (if I use Spring) or Vertx routes (if I use Vertx), about event bus and so on.
They just should write a business logic.
I would also like to generate in parallel classes for Typescript and Kotlin so I’ll make sure that frontend sends to the backend exactly the object that backend expects to receive.
No “unknown property” errors.
Here’s what we will generate.
We will take as an example some hypothetical web application, which can save and edit books.
A kind of library.
Technologies on backend — Kotlin, Vert.
Something that resembles what I showed in the article “Three paradigms of asynchronous programming in Vert.
x”To make it more interesting we will create DAO layer using Spring JPA.
I don’t say that it is good idea to mix Spring and Vert.
x (though I do this!), I just take Spring to show code generation for Entities.
Now we will create a project for this generation.
It will consist of 4 modules.
Now I will create them in the same git repository, but in real life every module will be placed in its own git repo, because they will change in different time and will have different versions.
So, the first project contains annotations for our endpoints, messages etc.
We will call it “metainfo”.
Two other projects — codegen and api depend on metainfo project.
api contains descriptions of endpoints and messages — classes which participate in frontend/backend interactionscodegen is the code generation project (but not a project where code is generated!) — it contains a code for gathering information from api project classes and code generators.
Generators will receive all details by command line arguments — in which package take endpoints and to which directory on hard disk write generated files, what is Velocity template file name etc.
So metainfo and codegen are generic projects — you can use them in different applications, while api is specific for a specific web application.
And here are two projects where we generate code:In frontend-generated we generate Typescript classes, which corresponds to Kotlin message and Entities classesAnd backend — our Vert.
x applicationIn order to make one project (gradle module) classes visible to another once we will use a gradle plugin for artifacts publication in a local Maven repository.
Metainfo project:Annotations to mark generation source — endpoints, messages, entities:For Typescript classes we will define annotations, which will mark messages fields, and which will be added to a generated Typescript classes:Here is a source code of metainfo project.
Project api:Pay attention to noArg and jpa plugins in build.
gradle for generation of no-argument constructors.
Due to our lack of imagination, we will create some crazy descriptions of endpoints and entities:Here is a source code of api project.
Codegen project:First, we will define “descriptors” — classes, which we will fill by information received from reflection on “api” project classes:Here’s the code which gathers information:Also, this project contains “main” classes, utility classes and so on.
You can see all of them in the repository.
In projects frontend-generated and backend we do similar things:1.
Dependency on api project on compilation stage.
dependency of build process on codegen project.
Generation templates are in the buildSrc directory, which is used in gradle for classes and resources, which are used for the build process, but not for compilation or runtime.
we can change generation template w/o recompilation of codegen project4.
frontend-generated compiles generated Typescript and publishes it to npm packages repository.
In backend project we generate Routers, which inherit from non-generated abstract Router, which knows how to handle various kinds of requests.
Also, we generate abstract Verticles, which you should inherit with business logic implementation.
Additionally we generate many boilerplate code — codecs registration, constants of addresses in event bus and so on — all those details, a programmer should not think about.
Here is the source code of frontend-generated and backend.
In frontend-generated pay attention to the plugin that publishes generated and compiled sources to a npm repo.
In order to make it work put the IP of your repository to build.
gradle and your authentication token to .
npmrc fileHere is how generated typescript classes look like:Pay attention to class-validator annotations.
In the backend project we also generate repositories for Spring Data JPA.
We can tell that message handling method in a verticle is blocking (and will be executed using Vertx.
executeBlocking) or asynchronous (with coroutines).
We can tell that a verticle generated for an Entity will be abstract and we will override hooks, which is called before and after generated methods.
We deploy all verticles automatically — just get all beans of Verticle type.
And this framework is easily extensible – for example we can put on an endpoint a list of user roles and generate a code, that checks that logged in user has one of allowed roles.
Also, by changing just templates we can generate akka-http instead of Vertx and Spring (or even Visual Basic 🙂 )Another direction — generate more frontend code, or swagger or WSDL or whatever you want.
All the source codes of those projects are here.
Thanks to Ildar from frontend team for his help.
.. More details