|
| 1 | +# Bootstrap python service |
| 2 | +[](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/ci-pipeline.yml) |
| 3 | +[](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.9.yml) |
| 4 | +[](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.10.yml) |
| 5 | +[](https://github.com/febus982/bootstrap-python-fastapi/actions/workflows/python-3.11.yml) |
| 6 | + |
| 7 | +[](https://codeclimate.com/github/febus982/bootstrap-python-fastapi/maintainability) |
| 8 | +[](https://codeclimate.com/github/febus982/bootstrap-python-fastapi/test_coverage) |
| 9 | +[](https://github.com/psf/black) |
| 10 | +[](https://github.com/charliermarsh/ruff) |
| 11 | + |
| 12 | +This is an example implementation of microservice applying |
| 13 | +concepts from [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) |
| 14 | +and [SOLID principles](https://en.wikipedia.org/wiki/SOLID). |
| 15 | + |
| 16 | +* The repository classes are isolated behind interfaces, enforcing the [Interface Segregation principle](https://en.wikipedia.org/wiki/Interface_segregation_principle) |
| 17 | + and the [Inversion of Control](https://en.wikipedia.org/wiki/Inversion_of_control) design pattern |
| 18 | +* The application frameworks are decoupled from the domain logic |
| 19 | +* The storage layer is decoupled from the domain logic |
| 20 | + |
| 21 | +In this way our components are loosely coupled and the application logic |
| 22 | +(the domains package) is completely independent of the chosen framework |
| 23 | +and the persistence layer. |
| 24 | + |
| 25 | +## HTTP API Docs and versioning |
| 26 | + |
| 27 | +API documentation is provided by [FastAPI](https://fastapi.tiangolo.com/features/) |
| 28 | +on `/docs` and `/redoc` paths using OpenAPI format. |
| 29 | + |
| 30 | +I believe that versioning an API at resource level provides a much more |
| 31 | +flexible approach than versioning the whole API. |
| 32 | + |
| 33 | +The example `books` domain provides 2 endpoints to demonstrate this approach |
| 34 | + |
| 35 | +* `/api/books/v1` (POST) |
| 36 | +* `/api/books/v2` (POST) |
| 37 | + |
| 38 | +## Package layers |
| 39 | + |
| 40 | +This application is structured following the principles of Clean Architecture. |
| 41 | +Higher level layers can import directly lower level layers. An inversion of control |
| 42 | +pattern has to be used for lower level layers to use higher level ones. |
| 43 | + |
| 44 | +Packages are ordered from the highest level to the lowest one. |
| 45 | + |
| 46 | +------ |
| 47 | + |
| 48 | +* `http_app` (http presentation layer) |
| 49 | +* `grpc_app` (grpc presentation layer) |
| 50 | +* `storage` (database connection manager, repository implementation) |
| 51 | + |
| 52 | +------ |
| 53 | + |
| 54 | +* `domains` (services, repository interfaces) |
| 55 | + |
| 56 | +------ |
| 57 | + |
| 58 | +## Class dependency schema |
| 59 | + |
| 60 | + |
| 61 | + |
| 62 | +## Data flow and layers responsibilities |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +## How to run |
| 67 | + |
| 68 | +Using Docker: |
| 69 | + |
| 70 | +* `make containers`: Build containers |
| 71 | +* `docker compose run --rm dev make migrate`: Run database migrations |
| 72 | +* `docker compose up dev`: Run HTTP application with hot reload |
| 73 | +* `docker compose up grpc`: Run GRPC application |
| 74 | +* `docker compose run --rm test`: Run test suite |
| 75 | + |
| 76 | +Locally: |
| 77 | + |
| 78 | +* `make migrate`: Run database migrations |
| 79 | +* `make install-dependencies`: Install requirements |
| 80 | +* `make dev-dependencies`: Install dev requirements |
| 81 | +* `make update-dependencies`: Updates requirements |
| 82 | +* `make migrate`: Run database migrations |
| 83 | +* `make dev`: Run HTTP application with hot reload |
| 84 | +* `make grpc`: Run GRPC application |
| 85 | +* `make test`: Run test suite |
| 86 | + |
| 87 | +## Other commands for development |
| 88 | + |
| 89 | +* `make generate-proto`: Generates grpcio python stubs from `.proto` files in `grpc_app/proto` directory |
| 90 | +* `make check`: Run tests, code style and lint checks |
| 91 | +* `make fix`: Run tests, code style and lint checks with automatic fixes (where possible) |
| 92 | + |
| 93 | +## Multistage dockerfile configuration |
| 94 | + |
| 95 | +Python docker image tend to become large after installing the application requirements |
| 96 | +(the slim base is ~150 MB uncompressed), therefore it's important to spend efforts |
| 97 | +to minimise the image size, even if it produces a slightly more complex multistage |
| 98 | +Dockerfile. |
| 99 | + |
| 100 | +The following setup makes sure the production image will keep to a minimal size ("only" 390MB): |
| 101 | + * 150MB base image |
| 102 | + * 165MB python installed dependencies |
| 103 | + * 73MB poetry + updated pip |
| 104 | + |
| 105 | +Using the following pipeline the "test" image is instead ~850MB, more than 400MB that would |
| 106 | +end up as a cost in traffic on each image pull. |
| 107 | + |
| 108 | + |
0 commit comments