-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Well, as we all know unlike frameworks like Django, which is battery included, Flask is a micro framework. That's what makes it flexible and the framework of choice for many developers. But flexibility comes with its own cons as well. Using Flask, one can actually up and run APIs in multiple ways. Which can leave the code base cluttered as the project grows and sometimes developers find hard time to structure their code base initially and end up with searching the web.
Being a lazy dev, I always want maximum result by writing less code. So while preparing a demo Flask app, my utmost priority was to maximise code reusability along with maintaining the flexibility of the APIs. After the demo I used this code as a starter of my new project and it took no time to up and run new app with basic functionality like JWT auth
, token blacklisting
, JSON logging
, adding X-Request-ID
and many more and most importantly it gave a proper shape to the codebase. As the project grows and I face new challenges, after solving them in the original project when I get time I update this nano framework for future reference.
I made it public hoping someone could get benefited out of it and if more people start using it and try to fit it in their specific need, they might encounter new challenges, which could help in generalising this boilerplate.
By following this dir structure, all the apps belong to a project can be managed in a single repo.
flask_boilerplate/
|--- demo/
|--- app_1/
|--- app_2/
...
flask_boilerplate
is the root dir of the project, which should contain the applications belong to the project but those apps are completely different, meaning those apps should not interact with each other. Each of these apps can follow monolith or micro services pattern.
Each app should adhere to following dir structure:
demo/
|--- deployment/
| |--- .env
| |--- docker-compose.yml
|
|--- services/
|--- svc_1
|--- svc_2
...
deployment/
dir contains docker-compose file which would start all the services present in services dir.
services/
contains all the services required by that application.
Ex:
In case of monolith: api, postgres, redis etc.
In case of micro services: api can be divided into its services like usr_mgmt_svc, gateway_svc, proj_mgmt_svc, postgres, redis etc.
NOTE: As these services would run in their respective containers, their code base can't be shared. So those services need to communicate through some sort of IPC. That may be simple REST API call, RPC or via messaging using MQ.
Now each service should adhere to following dir structure:
svc/
|--- data/
|--- infra/
|--- src/
data/
acts as temp or persistent storage which is required by the app run. Volume can be mount in this dir for persistent storage. For instance for service Postgres, one can attach a volume to the data dir, where all the Postgres DB content would reside.
infra/
contains the files which are required the run the particular service in a container. For example dockerfile, entrypoint script, gunicorn config etc.
src/
contains the actual source code for that service. For services like postgres, redis where docker images are directly used to start the service, this dir can be left empty.
If the above src/
contains Flask app, then that should follow below dir structure.
src/
|--- app/
|--- migrations/
|--- manage.py
|--- requirements.txt
migrations/
contains the Sqlalchemy migration scripts.
manage.py
is used to manage the Flask app. For example this file can be used to create custom cli commands to managing databases, seeding databases, running flask application or running unit tests etc.
requirements.txt
is the PIP requirements file. One can also create requirements dir and create multiple requirement files under that dir for different environments like req-base, req-dev, req-prod etc.
app/
contains the Flask application. It contains following files/dirs:
app/settings/
contains all the settings required for the Flask app. ex: logger, DB, JWT etc.
app/__init__.py
contains the factory method to create an Flask app instance with given environment.
app/services/
contains the services of the Flask app. If it is a monolith app, this dir could possibly contain services like user management, project/task management etc.
app/bootstrap_service.py
is provided to scaffold services like below:
$ python bootstrap_service.py
Service Name: test
About to create a service with name: service_test
Confirm? (y/N): y
services/service_test/controllers/ created.
services/service_test/models/ created.
services/service_test/tests/ created.
services/service_test/custom_exceptions/ created.
services/service_test/__init__.py created
services/service_test/urls.py created
NOTE: Add the service: 'service_test' to the INSTALLED_SERVICES in the settings file
As mentioned in the output, you need to add the created service name to the INSTALLED_SERVICES list present in the base.py
module inside the settings/
dir. It will automatically register the service blueprint to the Flask app.
Now you can add controllers for the service and list the routes in urls.py
module.
Below command will show all the routes registered to the Flask application.
$ python manage.py routes
Well, the provided docker-compose.yml file has made it fairly easy to build and run the docker containers with just one command.
$ docker-compose up --build <svc-name>
It will build the containers and start them after the build.
-d
[Optional] flag can be provided to run the containers in daemon mode.
svc-name
[Optional] service name(s) mentioned in the docker-compose.yml file can be provided to build/start that particular service. This is completely optional. If this is not provided, docker-compose will start all the listed services in the YML file.